与 javascript 递归中的参数状态混淆
Confusion with state of parameter in recursion with javascript
Gif for the debugger in console
所以,当 运行 这个时候,我有点困惑 fac
参数是如何保持状态的。我对 factorial(4) 的理解是
4 * (4-1) // 4 * 3 = 12
12 * (3-1) // 12 * 2 = 24
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
一些要记住的提示。
- 用结果替换函数调用时,将其括在括号中。
- 总是先简化最左边的表达式。这模仿了 JavaScript 的工作方式。
- 一次简化一个表达式。不要一步简化多个表达式。
Gif for the debugger in console
所以,当 运行 这个时候,我有点困惑 fac
参数是如何保持状态的。我对 factorial(4) 的理解是
4 * (4-1) // 4 * 3 = 12
12 * (3-1) // 12 * 2 = 24
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
一些要记住的提示。
- 用结果替换函数调用时,将其括在括号中。
- 总是先简化最左边的表达式。这模仿了 JavaScript 的工作方式。
- 一次简化一个表达式。不要一步简化多个表达式。