【问题标题】:Confusion with state of parameter in recursion with javascript与javascript递归中的参数状态混淆
【发布时间】:2021-10-24 01:56:17
【问题描述】:

Gif for the debugger in console

所以,当运行这个时,我有点困惑fac 参数是如何保持状态的。我对阶乘(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)

【问题讨论】:

  • 它没有保持任何状态,函数被递归调用了好几次
  • 那么,它如何理解参数的当前值是什么,我正在尽可能地清除。
  • 状态在JS为你管理的函数调用栈中。如果您将factorial 转换为不依赖堆栈的尾递归函数,您可能会更好地理解它。提示:您需要第二个参数作为发生乘法的累加器。
  • @Nick 当然它确实在递归调用期间保持fac 变量的状态

标签: javascript recursion functional-programming factorial


【解决方案1】:

factorial 的每次调用都有其自己的 fac 参数,具有自己的值。因此,在第一次通话中(factorial(4) 期间),当您这样做时:

return fac * factorial(fac - 1);

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

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

这里尝试显示调用如何叠加(使用缩进显示递归调用):

阶乘(fac = 4) 阶乘(fac = 3) 阶乘(fac = 2) 阶乘(fac = 1) 返回 1 | v 返回 2 * 1 = 2 | +−−−−−−−−+ | v 返回 3 * 2 = 6 | +−−−−−−−−+ | v 返回 4 * 6 = 24

注意factorial(1)运行时,内存中有四种不同fac参数,fac = 4(最外层调用),fac = 3(第一个递归调用),@ 987654340@(下一个递归调用)和fac = 1(返回开始前的最后一个递归调用)。

【讨论】:

    【解决方案2】:

    把函数调用想象成堆栈,所以当调用 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
    

    【讨论】:

      【解决方案3】:

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

        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 表达式呢?它将帮助您了解递归在函数式程序中的工作原理。无需考虑函数调用堆栈或其他任何技术。只需拿起笔和纸,就可以像在五年级数学课上那样简化表达式。

      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. 一次简化一个表达式。不要在一个步骤中简化多个表达式。

      【讨论】:

        猜你喜欢
        • 2015-04-27
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-17
        • 2016-10-03
        • 2017-05-07
        相关资源
        最近更新 更多