javascript/nodeJs 中的两个递归函数和计算器错误。了解差异

Two recursive functions and stackoverflow errors in javascript/nodeJs. Understanding the differences

查看 SICP 书和 JS 函数式编程,我创建了两个递归函数。我的期望是他们都引发了堆栈溢出错误。 但只有 sumAll() 函数引发了错误。请参阅下面函数 sumAll() 和 factorial() 的代码:

正如预期的那样,sumAll() 函数确实引发了堆栈溢出错误

function sumAll(n, i = 0, result = 0) {
  return (i > n)
    ? result
    : sumAll(n, i + 1, i + result);
}
    
console.log(sumAll(10000));

下面的 factorial() 函数没有引发堆栈溢出错误:

function factorial(n){
  return (n == 1)
    ? 1
    :  n* factorial((n-1))
}
    
console.log(factorial(10000))

我的问题是为什么 factorial() 函数不会引发堆栈溢出并在 nodeJS 中完美运行,而 sumAll() 也在 nodeJS 中引发堆栈溢出

I gave the below answer in error, I got mixed up about which function was throwing the exception. Please ignore.

你的第一个函数能够利用尾调用优化,而你的第二个函数不能(或者是,但可能不是以 node.js 语言实现的方式)。

考虑一下:您的第一个函数的通常条件是它以 return sumAll(n, i + 1, i + result) 结尾,这意味着一旦您得到 return 的内容,您就可以 return 那个。

然而,您的第二个函数以 return n* factorial((n-1)) 结尾,这意味着一旦您得到 return 的内容,您必须对其执行另一项操作(将其乘以 n),然后才能真正 return结果。

我相信 node.js 解释器无法尾调用优化第二个函数,因为它需要在 return.

之前对其执行另一个操作

请注意:我不确定这就是答案,而且我怀疑 node.js 可能不支持任何类型的尾调用优化。这是我的理论,但关于为什么一个函数可能会以这种方式出错而另一个函数不会。

局部变量的数量(即局部变量的内存)也在调用堆栈大小中考虑。

function computeMaxCallStackFrames(a, b, c, d) {
  try {
    return 1 + computeMaxCallStackFrames(a + 1, b + 1, c + 2, d + 3);
  } catch (e) {
    // Call stack overflow
    // console.log(e);
    return 1;
  }
}
var stackFrames = computeMaxCallStackFrames(1, 4, 6, 2);
console.log(stackFrames);

尝试增加编号。的参数,您会看到递归调用/调用堆栈帧数量的减少。

function computeMaxCallStackFrames(a, b, c, d, e) {
  try {
    return 1 + computeMaxCallStackFrames(a + 1, b + 1, c + 2, d + 3, e-2);
  } catch (e) {
    // Call stack overflow
    // console.log(e);
    return 1;
  }
}
var stackFrames = computeMaxCallStackFrames(1, 4, 6, 2, 9);
console.log(stackFrames);

零局部变量。

function computeMaxCallStackFrames() {
  try {
    return 1 + computeMaxCallStackFrames();
  } catch (e) {
    // Call stack overflow
    // console.log(e);
    return 1;
  }
}
var stackFrames = computeMaxCallStackFrames();
console.log(stackFrames);

因此,我们可以清楚地看到调用堆栈大小也考虑了局部变量的数量(即局部变量的内存)。如果有很多局部变量,那么没有。递归调用的次数会更少。

编辑:我们都知道堆栈大小会因浏览器而异。所以输出在不同的浏览器中不会相同,但在同一个浏览器中应该是一致的,即使我们 运行 它多次。

希望现在一切都有意义。