取自link,这是我在尝试解决这个问题时遇到的问题。
这是函数(修改了一下,试着帮助自己理解):
(function(){
fibonacci = (function () {
var cache = {};
return function (n) {
var cached = cache[n];
if (cached) {
console.log('already in the ', cache);
return cached;
}
if (n <= 1) {
console.log('no 0s or 1s, ', n);
return n;
}
console.log('a brand new ', n, 'consider yourself cached');
cache[n] = fibonacci(n - 2) + fibonacci(n - 1);
console.log('current cache: ', cache);
return cache[n];
};
}());
fibonacci(20);
})();
我稍微修改了一下以试图帮助自己理解,但是我迷失了因为输出变为0,然后它从0增加。我会想到在这句话中:
cache[n] = fibonacci(n - 2) + fibonacci(n - 1);
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}
谢谢,我知道递归可能是一个很大的菜鸟问题,而且我已经使用了几次,但了解它是如何工作的让我头晕目眩。
答案 0 :(得分:3)
“我不明白JavaScript会如何将这两个功能加在一起。”
JS不添加函数,它添加从这些函数调用返回的值。 假设计算f(n-2),当调用f(n-1)时,它将通过以下公式计算:
f(n-1) = f(n-2) + f(n-3)
到现在,由于我们计算了等式右边的两个值,因此两个值都将从缓存中获取。
让我们用一个例子来演示它,假设我们想要计算f(5):
f(5) = f(4) + f(3)
用f(3)递归调用:
f(3) = f(2) + f(1)
递归电话:
f(1) = 1 and f(2) = cached(f(1)) + f(0) = 1 + 0 = 1
现在我们回到calc f(3)并且我们同时缓存了值f(2)和f(1),因此:
f(3) = 1 + 1 = 2
回到calc f(4)
f(4) = f(3) + f(2) = 2 + 1 = 3
再次结束:
f(5) = f(4) + f(3) = 3 + 2 = 5
密切注意这样一个事实:一旦你到达递归中的最深点(f(1)),将使用一直备份缓存,并且不会计算任何值,这使得这个实现非常高效!
答案 1 :(得分:1)
如果代码被简化并且您从较小的数字开始,比如说4,您可能会发现它更容易。以下功能与您最初发布的功能完全相同:
var cache = {};
function fibonacci(n) { // assume n = 4
var cached = cache[n];
第一次,cache [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;
}
然后执行以下行:
console.log('a brand new ', n, 'consider yourself cached'); // 4
cache[n] = fibonacci(n - 2) + fibonacci(n - 1);
是:
cache[4] = fibonacci(2) + fibonacci(3);
在上面的行中,首先评估左侧,开始创建cache
的'4'属性。它实际上并没有创建,因为语句没有完成,所以你几乎有:
cache = {4:undefined};
然后评估右侧以查看将分配的内容。由于存在+
运算符,因此必须对这两个表达式进行求值,以确定它是否被视为加法或连接。
评估下一个fibonacci(2)
(+
是加法还是连接,评估从左到右),所以重复上述过程,创建:
cache[2] = fibonacci(0) + fibonacci(1);
你几乎有:
cache = {4:undefined, 2:undefined};
注意到实际上还没有创建属性。
现在评估fibonacci(0)
。这次它到达第二个if
并返回0,因此没有创建cache['0']
,现在你有了:{/ p>
cache[2] = 0 + fibonacci(1);
同样,在评估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);
第一个表达式从第一个1
返回if
,因此您拥有:
cache[3] = 1 + fibonacci(2);
第二个表达式到达第一个if,其中cache [2]存在,所以它返回1(即cache [2]的值),你有:
cache[3] = 1 + 1; // cache = {3:1, 3:2, 4:undefined};
然后返回缓存[3](即2),所以你回到了:
cache[4] = 1 + 2;
现在有缓存= {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));