代码之家  ›  专栏  ›  技术社区  ›  Paweł

当递归函数在javascript中可计数时,最大调用堆栈大小超出了RangeError

  •  1
  • Paweł  · 技术社区  · 8 年前

    我创建了一个脚本,用选定的小数位数来划分数字。除非我设置了大量的小数位,否则一切正常。该文件通过节点运行。当十进制数数组长度达到~2400时,js放弃,Chrome在~1900时放弃。

    我简化了代码,下面的脚本示例 Maximum call stack size exceeded 在上面 ~20000 十进制数数组长度。我认为当循环或递归函数被无休止地调用时会抛出这个错误,但在我的例子中,迭代次数是可计数的。这可能是一个巨大的数字,但我的模块旨在对大数字进行数学运算。

    为什么会发生这种情况?我能避免发生这种错误吗?

    var decimals = [];
    var max = 20000;
    
    recurse();
    
    function recurse() {
      decimals.push(Math.floor(Math.random()*10));
      if(decimals.length === max) return;
      recurse();
    }
    3 回复  |  直到 8 年前
        1
  •  2
  •   Matus Dubrava    8 年前

    不,您可以对调用堆栈进行多少函数调用是有限制的,因此即使递归函数有一个可到达的基本情况,如果递归调用的数量太多,它仍然会抛出错误。

    一种解决方法是使用一种叫做 trampoline 不直接在函数内部执行递归调用,而是返回一个新函数,然后在循环中执行,直到达到基本情况。

    使用这种技术,您的函数可以执行任意数量的递归调用,因为您不会同时将更多的函数调用放入调用堆栈,因此不会溢出。

    var decimals = [];
    var max = 20000;
    
    function _recurse(){
      decimals.push(Math.floor(Math.random()*10));
      if(decimals.length === max) return;
      return () => _recurse();
    }
    
    const trampoline = fn => (...args) => {
      let res = fn(...args);
      while (typeof res === 'function') { res = res(); }
      return res;
    }
    
    const recurse = trampoline(_recurse);
    
    recurse()
    
    console.log(decimals);

    请注意,您的问题可以通过一种更简单的方式解决,即使用循环,而无需使用递归。例如:

    function createRandomSequence(amount) {
      const decimals = [];
      for (let i = 0; i < amount; i++) {
        decimals.push(Math.floor(Math.random()*10));
      }
      return decimals;
    }
    
    console.log(createRandomSequence(10));
        2
  •  2
  •   Jonas Wilms    8 年前

    只需使用常规循环,或类似的方法:

     const random = length => Array.from({ length }, () => Math.floor(Math.random() * 10));
    
    const decimals = random(20000);
    
        3
  •  1
  •   KooiInc    8 年前

    关于调用堆栈:请参阅 this web page

    如果你想要的是20000(伪随机)小数,你也可以使用类似的方法:

    var maxDecimals = 20000;
    var decimals = Array.from({length: maxDecimals})
      .map(v => Math.floor(Math.random()*10));
      
    console.log(decimals);