Javascript 有人能给我解释一下将这个递归函数添加到它本身是如何工作的吗?

Javascript 有人能给我解释一下将这个递归函数添加到它本身是如何工作的吗?,javascript,recursion,Javascript,Recursion,这是我在想办法解决这个问题时遇到的 下面是函数(稍作修改以尝试帮助自己理解): fibonacci(n-2)将被计算,然后紧接着fibonacci(n-1)。 但即使是这样,我也不明白JavaScript如何将这两个函数添加到一起 有谁能帮我理解一下这是怎么回事,或者至少,你能帮我以一种更容易理解的方式重新构造它吗 以下是输出: a brand new 20 consider yourself cached a brand new 18 consider yourself cached

这是我在想办法解决这个问题时遇到的

下面是函数(稍作修改以尝试帮助自己理解):

fibonacci(n-2)
将被计算,然后紧接着
fibonacci(n-1)

但即使是这样,我也不明白JavaScript如何将这两个函数添加到一起

有谁能帮我理解一下这是怎么回事,或者至少,你能帮我以一种更容易理解的方式重新构造它吗

以下是输出:

a brand new  20 consider yourself cached 
a brand new  18 consider yourself cached 
a brand new  16 consider yourself cached 
a brand new  14 consider yourself cached 
a brand new  12 consider yourself cached 
a brand new  10 consider yourself cached 
a brand new  8 consider yourself cached 
a brand new  6 consider yourself cached 
a brand new  4 consider yourself cached 
a brand new  2 consider yourself cached 
no 0s or 1s,  0 
no 0s or 1s,  1 
current cache:  Object {2: 1} 
a brand new  3 consider yourself cached 
no 0s or 1s,  1 
already in the  Object {2: 1} 
current cache:  Object {2: 1, 3: 2} 
current cache:  Object {2: 1, 3: 2, 4: 3} 
a brand new  5 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3} 
already in the  Object {2: 1, 3: 2, 4: 3} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8} 
a brand new  7 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21} 
a brand new  9 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55} 
a brand new  11 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144} 
a brand new  13 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377} 
a brand new  15 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987} 
a brand new  17 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597, 18: 2584} 
a brand new  19 consider yourself cached 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597, 18: 2584} 
already in the  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597, 18: 2584} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597, 18: 2584, 19: 4181} 
current cache:  Object {2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55, 11: 89, 12: 144, 13: 233, 14: 377, 15: 610, 16: 987, 17: 1597, 18: 2584, 19: 4181, 20: 6765} 
谢谢,我知道递归可能是一个很大的noob问题,我已经用过好几次了,但了解它的工作原理让我头晕目眩。

“我不明白JavaScript如何将这两个函数添加到一起。”

JS不添加函数,而是添加从这些函数调用返回的值。 假设f(n-2)已计算,当调用f(n-1)时,将通过以下公式计算:

f(n-1) = f(n-2) + f(n-3)
现在,由于我们在等式右侧计算了这两个值,所以这两个值都将从缓存中获取

让我们用一个例子来演示,假设我们要计算f(5):

具有f(3)的递归调用:

递归调用:

f(1) = 1 and f(2) = cached(f(1)) + f(0) = 1 + 0 = 1
现在我们回到计算f(3),我们缓存了f(2)和f(1),因此:

f(3) = 1 + 1 = 2
返回计算f(4)

再次结束:

f(5) = f(4) + f(3) = 3 + 2 = 5

请密切注意这样一个事实,即一旦到达递归的最深处(f(1)),就会一直使用缓存备份,并且不会计算任何值,这使得此实现非常高效

如果代码简化,并且从较小的数字开始,例如4,您可能会发现这更容易。以下功能与您最初发布的功能相同:

var cache = {};

function fibonacci(n) {   // assume n = 4
   var cached = cache[n];
第一次通过时,缓存[4]将未定义,因此以下测试的结果为false:

    if (cached) {
        console.log('already in the ', cache);
        return cached;
    }
当n=4时,以下也为假:

    if (n <= 1) {
        console.log('no 0s or 1s, ', n);
        return n;
    }
即:

    cache[4] = fibonacci(2) + fibonacci(3);
在上行中,首先计算左侧,开始创建
缓存的'4'属性。由于语句尚未完成,它实际上尚未创建,因此您几乎有:

然后计算右侧,以查看将分配什么。因为有一个
+
运算符,所以必须对这两个表达式求值,以查看它是被视为加法还是级联

接下来对
fibonacci(2)
进行求值(无论
+
是加法还是缩合,求值从左到右进行),因此重复上述过程,创建:

    cache[2] = fibonacci(0) + fibonacci(1);
你几乎做到了:

请注意,这些属性尚未实际创建

现在计算
fibonacci(0)
。这次它到达第二个
if
并返回0,因此不会创建
缓存['0']
,现在您有:

cache[2] = 0 + fibonacci(1);
    cache[3] = 1 + fibonacci(2);
    cache[3] = 1 + 1; // cache = {3:1, 3:2, 4:undefined};
同样,在计算
fibonacci(1)
时,第二个
if
语句执行并返回
1
,因此您有一个要分配的值,以便创建属性并分配值:

    cache[2] = 0 + 1; // cache = {2:1, 4:undefined}; 
现在转到下一行:

    console.log('current cache: ', cache);
    return cache[n]; // returns 1;
}
现在,前面的通话继续:

    cache[4] = 1 + fibonacci(3);
这又是一次:

    cache[3] = fibonacci(1) + fibonacci(2);
第一个表达式从第一个
if
返回
1
,因此:

cache[2] = 0 + fibonacci(1);
    cache[3] = 1 + fibonacci(2);
    cache[3] = 1 + 1; // cache = {3:1, 3:2, 4:undefined};
第二个表达式到达第一个if,其中缓存[2]存在,因此它返回1(即缓存[2]的值),您有:

cache[2] = 0 + fibonacci(1);
    cache[3] = 1 + fibonacci(2);
    cache[3] = 1 + 1; // cache = {3:1, 3:2, 4:undefined};
然后返回缓存[3](即2),因此返回:

    cache[4] = 1 + 2;
现在有cache={2:1,3:2,3:3}

您可以将上述内容作为顺序操作编写(所有递归函数都可以作为顺序操作编写),这在速度很重要的情况下很常见,因为顺序操作总是更快。在这种情况下,顺序函数非常简单:

function fibonacci2(n) {
  var fibs = {0:0, 1:1};
  var i = 1;
  while (i < n) {
    fibs[++i] = fibs[i-1] + fibs[i-2];
  }
  return fibs;
}

console.log(fibonacci2(4));
函数fibonacci2(n){
var fibs={0:0,1:1};
var i=1;
而(i
这是一个基本正确的答案-但是Javascript不会在
f(3)
之前计算
f(4)
f(4)+f(3)
?@Patashu调用的顺序无关紧要,因为这个递归是自下而上计算的,所以f(3)总是在f(4)之前计算。令人困惑是的,有一点,但是试着像我那样用数字手动运行它,你会看到;)哦,是的,我把计算顺序和缓存顺序搞混了。非常感谢您的回答,我需要一点时间来消化它。请注意,您正在递归之前记录“认为自己已缓存”,因此这是实际缓存发生之前的方式。您可能需要使用Firebug的
console.group()
功能来更好地记录日志。示例:非常感谢您的深入解释,因为它确实帮助我理解了输出。这将是几个小时之前,我得到测试出来,但我只是想表达我的赞赏马上!我认为这已经解决了,因为这正是我想要的,而且对我来说更容易理解。再次感谢您的帮助。经过一段时间的讨论,我想我的主要困惑是当n变为0时会发生什么。出于某种原因,我忘记了当函数返回时,函数只是将自身转换为返回值,这使得使用“+”运算符对两个函数的求值更容易理解。。。(即使递归仍然让我有点困惑)
    cache[4] = 1 + 2;
function fibonacci2(n) {
  var fibs = {0:0, 1:1};
  var i = 1;
  while (i < n) {
    fibs[++i] = fibs[i-1] + fibs[i-2];
  }
  return fibs;
}

console.log(fibonacci2(4));