为什么这个memoization实现可以在匿名函数上工作,但不能在声明的函数上工作?

时间:2014-08-21 21:41:11

标签: javascript recursion memoization

我正在尝试使用memoization来优化Fibonacci函数的显式自递归实现。以下是相当标准的实现(虽然关注实际问题,但这是一个简单且相当天真的实现)。

Function.prototype.memoize = function () {
    var originalFunction = this,
        slice = Array.prototype.slice;
        cache = {};
    return function () {
        var key = slice.call(arguments);
        if (key in cache) {
            return cache[key];
        } else {
            return cache[key] = originalFunction.apply(this, key);
        }
    };
};

现在,在创建和记忆函数时,如下所示,这适用于 1 (情景1)

var fibonacci = function (n) {
    return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}.memoize();

console.log(fibonacci(100));

但是,以下情况并非如此。 2 (情景2)

var fibonacci = function (n) {
    return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};

console.log(fibonacci.memoize()(100));

这两者都没有。 2 (情景3)

function fibonacci(n) {
    return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(fibonacci.memoize()(100));

我的假设是,由于在函数上调用memoize()的方式不同,因此正在发生变化。请注意,功能在其他方面是相同的。我想这可能是因为除了第一个例子之外,只有第一个调用被记忆,而不是递归调用。

问题

如果我的上述假设确实正确,那为什么会发生这种情况?有人可以详细解释后两种情况与第一种不同的情况吗?

1 在这个实例中工作意味着返回第100个Fibonacci数,因为只有在使用memoization时才可以递归计算它。

2 不工作就是让浏览器崩溃。

1 个答案:

答案 0 :(得分:1)

是的,在第二和第三种情况下只记住第一个电话是正确的。

在第一个场景中,对原始函数的引用仅作为值存在,然后memoize应用于该值,fibonacci变量包含对memoized函数的引用。

在第二和第三种情况中,fibonacci是对原始函数的引用。包含对memoized函数的引用的表达式fibonaci.memoize()的值仅在调用一次之前作为值存在。

memoize方法不会更改原始函数,而是返回包装原始函数的新函数。原始函数没有改变,要使用memoization,你必须调用memoize方法返回的函数。

在函数对fibonacci进行递归调用的第一个场景中,它是使用的memoized函数。在进行递归调用的第二个和第三个场景中,fibonacci是原始函数。