代码之家  ›  专栏  ›  技术社区  ›  guest271314

数字(整数或十进制)到数组,数组到数字(整数或十进制),不使用字符串

  •  8
  • guest271314  · 技术社区  · 7 年前

    要求:

    将输入整数或小数转换为数组,并将可能包含小数的整数数组转换为数字。

    在过程中,不要使用字符串方法,也不要将输入或输出转换为字符串(在编写的代码的每个版本中都会遵循一个自我施加的限制)。

    上下文和用例

    BigInt 在某些浏览器中可用,但不是 BigDecimal

    调整能力 整数或小数的位数,通过在 N 数组的索引,尝试解决 OEIS A217626 例如,直接

    ~~(128.625*9*1.074)//1243
    ~~(128.625*9*1.144)//1324
    

    当前规范为WIP,在描述输入小数部分的相关处理时可能被视为具有挑战性,特别是在有前导零的情况下。

    Input <----------> Output
    
    -123               [-1,-2,-3]
    4.4                [4,0.4]
    44.44              [4,4,0.4,4]
    -0.01              [-0.01]
    123                [1,2,3]
    200                [2,0,0]
    2.718281828459     [2,0.7,1,8,2,8,1,8,2,8,4,5,8,9]
    321.7000000001     [3,2,1,0.7,0,0,0,0,0,0,0,0,1]
    809.56             [8,0,9,0.5,6]
    1.61803398874989   [1,0.6,1,8,0,3,3,9,8,8,7,4,9,8,9]
    1.999              [1,0.9,9,9]
    100.01             [1,0,0,0.01]
    545454.45          [5,4,5,4,5,4,0.4,5]
    -7                 [-7]
    -83.782            [-8,-3,-0.7,-8,-2]
    1.5                [1,0.5]
    100.0001           [1,0,0,0.0001]
    

    [...Math.E] -> [2, 0.7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9] -> 2.718281828459
    

    Number.prototype[Symbol.iterator] numberToArray .

    准则的最新版本(一些概念和准则的原始版本基于 Get decimal portion of a number with JavaScript Converting int value to String without using toString and parseInt method ; Convert integer to array of digits arrayToNumber 100.05010000000497 应该是 100.00015 -83.082 应该是 -83.782

    function numberToArray(n) {
    
      if (Math.abs(n) == 0 || Math.abs(n) == -0) {
        return [n]
      }
    
      const r = [];
    
      let [
        a, int = Number.isInteger(a), d = g = [], e = i = 0
      ] = [ n || this.valueOf()];
    
      if (!int) {
        let e = ~~a;
        d = a - e;
        do {
          if (d < 1) ++i;
          d *= 10;
        } while (!Number.isInteger(d));
      }
    
      for (; ~~a; r.unshift(~~(a % 10)), a /= 10);
    
      if (!int) {
        for (; ~~d; g.unshift(~~(d % 10)), d /= 10);
        g[0] = g[0] * (1 * (10 ** -i))
        r.push(...g);
      }
    
      return r;
    
    }
    
    function arrayToNumber(a) {
      if ((Math.abs(a[0]) == 0 || Math.abs(a[0]) == -0) 
         && a.length == 1) return a[0];
      const [
        g, r = x => x.length == 1 
                    ? x[0] 
                    : x.length === 0 
                      ? x 
                      : x.reduce((a, b) => a + b)
        , b = a.find(x => g(x)), p = a.findIndex(x => g(x))
      ] = [x => !Number.isInteger(x)];
    
      let [i, j] = [b ? p : a.length, -1];
    
      return a.length === 1 
        ? a[0] 
        : b && p 
          ? r(a.slice(0, p).map(x => i ? x * (10 ** --i) : x)) 
            + (a[p] + (a[p + 1] !== undefined 
              ? r(a.slice(p + 1).map(x => x * (10 ** --j))) 
              : 0)) 
          : r(a.map(x => i ? x * (10 ** --i) : x))
    }
    
    let tests = [0, 200, 100.00015, -123, 4.4, 44.44, -0.01, 123
                , 2.718281828459, 321.7000000001, 809.56
                , 1.61803398874989, 1.999, 100.01, 545454.45
                , -7, -83.782, 12, 1.50, 100.0001];
    
    let arrays = tests.map(n => [...numberToArray(n)]);
    
    let numbers = arrays.map(n => arrayToNumber(n));
    
    console.log({tests, arrays, numbers});

    问题:

    1. 如何修复现有代码中列出的bug?
    2. 在不使用字符串方法或在过程中将输入或输出转换为字符串的限制下,代码是否可以以不同的方式进行改进或组合以满足要求?
    3. 当前规范是否可以改进所用术语的清晰度,以避免对小数的预期输出产生混淆?
    1 回复  |  直到 7 年前
        1
  •  9
  •   Community Mohan Dere    6 年前

    numberToArray() :

    我已经为您的实现工作了一段时间,并考虑首先分析 numberToArray() 方法首先,我决定创建一个分析十进制数并返回的方法 statistics 关于它,基本上是您从代码的这一部分获得的信息:

    if (!int) {
        let e = ~~a;
        d = a - e;
        do {
            if (d < 1) ++i;
            d *= 10;
        } while (!Number.isInteger(d));
    }
    

    我使用的方法是下一个(将在内部使用) numberToArray() )基本上可以得到下一个信息:

    1) iSection )十进制数的整数。

    2) 小数部分( dSection

    3) 点后的位数( dDigits ).

    点后前导零的数目( dZeros ).

    function getDecimalStats(dec)
    {
        let dDigits = 0, test = dec, factor = 1, dZeros = 0;
    
        // Store the integer section of the decimal number.
    
        let iSection = ~~dec;
    
        // Get the numbers of digits and zeros after the comma.
        
        while (!Number.isInteger(test))
        {
            factor = Math.pow(10, ++dDigits);
            test = dec * factor;
            dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
        }
    
        // Store the decimal section as integer.
    
        let dSection = test - (iSection * factor);
    
        // Return an object with all statistics.
    
        return {iSection, dSection, dZeros, dDigits};
    };
    
    console.log(getDecimalStats(10.001));
    console.log(getDecimalStats(-210.1));
    console.log(getDecimalStats(-0.00001));
    .as-console {background-color:black !important; color:lime;}
    .as-console-wrapper {max-height:100% !important; top:0;}

    当然,如果你不喜欢,你可以把同样的逻辑直接放进去 numberToArray() correction factor numberToArray() 方法

    function getDecimalStats(dec)
    {
        let dDigits = 0, test = dec, factor = 1, dZeros = 0;
    
        // Store the integer section of the decimal number.
    
        let iSection = ~~dec;
    
        // Get the numbers of digits and zeros after the comma.
        
        while (!Number.isInteger(test))
        {
            factor = Math.pow(10, ++dDigits);
            test = dec * factor;
            dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
        }
    
        // Store the decimal section as integer.
    
        let dSection = test - (iSection * factor);
    
        // Return an object with all statistics.
    
        return {iSection, dSection, dZeros, dDigits};
    };
    
    function numberToArray(n)
    {
        let r = [];
    
        if (Math.abs(n) == 0)
            return [n];
    
        let [a, int = Number.isInteger(a), g = []] = [n || this.valueOf()];
    
        // Get the stats of the decimal number.
    
        let {dSection, dZeros} = getDecimalStats(a);
    
        // Push the integer part on the array.
    
        for (; ~~a; r.unshift(~~(a % 10)), a /= 10);
    
        // Push the decimal part on the array.
    
        if (!int)
        {
            // Push decimal digits on temporal array "g".
            for (; ~~dSection; g.unshift(~~(dSection % 10)), dSection /= 10);
    
            // Define the correction factor for the next operation.
            let cf = 10 ** (++dZeros);
    
            // Map g[0] to a decimal number and push elements on the array.
            g[0] = (g[0] * cf) * ((10 ** -dZeros) * cf) / (cf * cf);
            r.push(...g);
        }
    
        return r;
    }
    
    let tests = [
    0, 200, 100.00015, -123, 4.4, 44.44, -0.01, 123,
    2.718281828459, 321.7000000001, 809.56,
    1.61803398874989, 1.999, 100.01, 545454.45,
    -7, -83.782, 12, 1.50, 100.0001
    ];
    
    let arrays = tests.map(n => [...numberToArray(n)]);
    console.log({tests, arrays});
    .as控制台{背景颜色:黑色!重要;颜色:石灰;}

    方法 arrayToNumber()

    对于这一个,我决定自己去(实际上忽略了你当前的逻辑)。下一种方法将使用前面提到的方法 getDecimalStats() 主要是 Array::reduce()

    function getDecimalStats(dec)
    {
        let dDigits = 0, test = dec, factor = 1, dZeros = 0;
    
        // Store the integer section of the decimal number.
    
        let iSection = ~~dec;
    
        // Get the numbers of digits and zeros after the comma.
        
        while (!Number.isInteger(test))
        {
            factor = Math.pow(10, ++dDigits);
            test = dec * factor;
            dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
        }
    
        // Store the decimal section as integer.
    
        let dSection = test - (iSection * factor);
    
        // Return an object with all statistics.
    
        return {iSection, dSection, dZeros, dDigits};
    };
    
    function arrayToNumber(a)
    {
        // Get the index of the first decimal number.
    
        let firstDecIdx = a.findIndex(
            x => Math.abs(x) > 0 && Math.abs(x) < 1
        );
    
        // Get stats about the previous decimal number.
    
        let {dZeros} = getDecimalStats(firstDecIdx >= 0 ? a[firstDecIdx] : 0);
    
        // Normalize firstDecIdx.
    
        firstDecIdx = firstDecIdx < 0 ? a.length : firstDecIdx;
    
        // Reduce the array to get the number.
        
        let number = a.reduce(
            ({num, dIdx, dPow}, n, i) =>
            {
                // Define the correction factor.
                let cf = 10 ** (dPow + i - dIdx);
    
                if (i < dIdx)
                   num += n * (10 ** (dIdx - i - 1));
                else if (i === dIdx)
                   num = ((num * cf) + (n * cf)) / cf;
                else
                   num = ((num * cf) + n) / cf;
    
                return {num, dIdx, dPow};
            },
            {num: 0, dIdx: firstDecIdx, dPow: ++dZeros}
        );
    
        return number.num;
    }
    
    let tests = [
        [0],
        [2, 0, 0],
        [1, 0, 0, 0.0001, 5],
        [-1, -2, -3],
        [4, 0.4],
        [4, 4, 0.4, 4],
        [-0.01],
        [1, 2, 3],
        [2, 0.7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9],
        [3, 2, 1, 0.7, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [8, 0, 9, 0.5, 6],
        [1, 0.6, 1, 8, 0, 3, 3, 9, 8, 8, 7, 4, 9, 8, 9],
        [1, 0.9, 9, 9],
        [1, 0, 0, 0.01],
        [5, 4, 5, 4, 5, 4, 0.4, 5, 0],
        [-7],
        [-8,-3, -0.7, -8, -2],
        [1, 2],
        [1, 0.5],
        [1, 0, 0, 0.0001]
    ];
    
    let numbers = tests.map(n => arrayToNumber(n));
    console.log(numbers);
    .as控制台{背景颜色:黑色!重要;颜色:石灰;}
    .作为控制台包装{最大高度:100%!重要;顶部:0;}

    最后,我希望您能重视我的努力,显然我的解决方案可以有很多改进(因此,欢迎任何建议)。例如,目前没有或很少进行安全检查。