与 javascript 递归中的参数状态混淆

Confusion with state of parameter in recursion with javascript

Gif for the debugger in console

所以,当 运行 这个时候,我有点困惑 fac 参数是如何保持状态的。我对 factorial(4) 的理解是

  1. 4 * (4-1) // 4 * 3 = 12
  2. 12 * (3-1) // 12 * 2 = 24
  3. 24 * (2-1) // 24 * 1 = 24
function factorial(fac){
  debugger
  if(fac == 1)
    return fac
  return fac * factorial(fac-1)
}
factorial(4)

factorial 的每次调用都有其自己的 fac 参数,具有自己的值。因此,在第一次调用时(在 factorial(4) 期间),当您执行以下操作时:

return fac * factorial(fac - 1);

再次调用 factorial,传入由 fac - 1 (3) 产生的值。该新呼叫接收 fac = 3 并对其进行处理。发生这种情况时,第一个调用正在等待从中获取 return 值(我们称之为 retVal),因此它可以通过将 retVal 插入其中 retVal 来完成 return fac * ____________ =21=] 是.

最终,对 factorial 的调用通过 returning fac 而不是再次调用 factorial 来完成它的工作,这意味着调用它的人可以完成它的工作work 和 return 它的 return 值,这意味着调用 it 的那个可以做到这一点,直到我们回到顶部 return这是总体结果。

下面是显示调用如何叠加的尝试(使用缩进显示递归调用):

factorial(fac = 4)
    factorial(fac = 3)
        factorial(fac = 2)
            factorial(fac = 1)
            return 1
                   |
                   v
        return 2 * 1 = 2
                       |
               +−−−−−−−+
               |
               v
    return 3 * 2 = 6
                   |
           +−−−−−−−+
           |
           v
return 4 * 6  = 24

注意当factorial(1)为运行时,内存中有四个不同的 fac参数,fac = 4(最外层调用)、fac = 3(第一个递归调用)、fac = 2(下一个递归调用)和 fac = 1(return 开始之前的最后一个递归调用) .

把函数调用想象成栈,所以当调用 fac(4) 时,这个栈就形成了

factorial(1)
     ^
Calls^
     ^
factorial(2)
     ^
Calls^
     ^  
factorial(3)
     ^
Calls^
     ^
factorial(4)

那么这个栈就是自上而下解析的。所以每次调用都会更改 fac 的值

factorial(1) ==> fac = 1, return value =1 
then resolves 
factorial(2) ==> fac = 2, return value =2*factorial(1) > already in memory
then resolves
factorial(3) ==> fac = 3, return value =3*factorial(2) -> in memory
then resolves
factorial(4) ==> fac = 4, return value =4*factorial(3) -> in memory

您可以像简化数学表达式一样简化纯表达式。

  factorial(4)
= 4 * factorial(4 - 1)             // because 4 != 1
= 4 * factorial(3)
= 4 * (3 * factorial(3 - 1))       // because 3 != 1
= 4 * (3 * factorial(2))
= 4 * (3 * (2 * factorial(2 - 1))) // because 2 != 1
= 4 * (3 * (2 * factorial(1)))
= 4 * (3 * (2 * 1))                // because 1 == 1
= 4 * (3 * 2)
= 4 * 6
= 24

这是一种非常强大的表达式推理方法。这是 one of the advantages 编写纯代码而不是不纯代码。您不能以这种方式简化不纯的表达式,因为不纯的代码具有需要跟踪的副作用。

无论如何,你为什么不尝试自己简化以下 result 表达式?它将帮助您了解递归在函数式程序中的工作原理。无需考虑函数调用堆栈或任何其他技术问题。拿起笔和纸开始简化表达式,就像您在 5 年级数学中所做的那样 class。

function fib(n) {
    if (n === 0) return 1;
    if (n === 1) return 1;
    return fib(n - 1) + fib(n - 2);
}

const result = fib(5);

console.log(result); // 8

一些要记住的提示。

  1. 用结果替换函数调用时,将其括在括号中。
  2. 总是先简化最左边的表达式。这模仿了 JavaScript 的工作方式。
  3. 一次简化一个表达式。不要一步简化多个表达式。