关于“JavaScript - 好零件”示例的说明(第4.15节)?

时间:2010-09-26 17:04:02

标签: javascript memoization

JS中的初学者:)需要解释Crockford's book中的代码段,第4.15节:

var memoizer = function (memo, fundamental) {
    var shell = function (n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fundamental(shell, n);
            memo[n] = result;
        }
        return result;
    };
    return shell;
};

var fibonacci = memoizer([0, 1], function (shell, n) {
    return shell(n - 1) + shell(n - 2);
});

问题:我们如何计算斐波那契(15),如果它是简单的斐波那契(15)调用,那它的工作原理如何?

感谢您的帮助。

4 个答案:

答案 0 :(得分:3)

这里有一个console.log()带注释的版本,它试图显示堆栈如何返回并为每个相应的递归调用将(n-1)+(n-2)的结果分配给memo数组。还要记住堆栈以相反的顺序返回。因此,在记录的输出中,您将看到最后一次返回的呼叫:

var memoizer = function (memo, fundamental) {
    var shell = function (n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fundamental(shell, n);
            console.log("Hence 'shell(n-1)+shell(n-2)' results in the assignment memo["+n+"]="+result);
            memo[n] = result;
        }
        return result;
    };
    return shell;
};
var fibonacci = memoizer([0, 1], function (shell, n) {
    console.log("shell is called, and 'n' is equal to --> " + n + "\n" + "At this point shell(n-1)="+shell(n-1)+" AND shell(n-2)="+shell(n-2));
    return shell(n - 1) + shell(n - 2);    
});

答案 1 :(得分:2)

看起来您对调用fibonacci(15)的工作原理感到困惑。 让我们简化代码(忘记备忘录一秒钟)。

var m = function () {
    var s = function (n) {
        console.log(n);
    };
    return s;
}; 
var f = m();

基本上我们将f设置为函数m()的返回值。在这种情况下,该返回值是一个函数。看,我们可以进一步简化它:

var f = function (n) { console.log(n); };

换句话说,我们将f设置为一个接受一个参数的函数。我们在fibinacci示例中做了同样的事情。这就是调用fibonacci(15)有效的原因。

答案 2 :(得分:1)

要评估该功能,您只需要调用它:

fibonacci(15);

如果您想查看结果,最简单的方法是:

alert(fibonacci(15));

如果您想更频繁地执行此操作,请下载Firebug,并在脚本底部执行此操作:

Console.log(fibonacci(15));

或者直接在Firebug控制台中输入,然后按return:

fibonacci(15)

答案 3 :(得分:1)

正如您对问题的评论所表明的那样,您应该在调试器中查看代码,以便在不能按照本书中的说明进行操作时更好地了解正在发生的事情。但我会简要介绍一下发生了什么:

正在演示的是“memoisation”,它是函数式编程中常用的优化技术。如果结果仅取决于传递给它的参数,则称该函数是纯函数。因此,如果函数是纯函数,则可以根据参数缓存结果 - 此技术称为memoisation。如果函数计算成本很高并且多次调用,则会执行此操作。

用于演示此(如此处)的经典示例是生成Fibonacci numbers。我不会详细说明这些是如何解决的,但基本上当你越来越高的数字时,你会越来越多地重复自己,因为每个数字都来自前面两个数字。通过记忆每个中间结果,您只需计算一次,从而使算法更快(当您向上移动序列时更快,更快)。

就这段代码而言,memoizer有两个参数 - 'memo',它是缓存。在这种情况下,它将使用已经填充在'[0,1]'中的前两个值 - 这些是前两个Fibonacci数。

第二个参数是应用记忆的功能。在这种情况下,递归Fibonacci函数:

function(shell,n){     返回shell(n - 1)+ shell(n - 2); }

即。结果是序列中前两个数字的总和。

备忘录首先检查它是否已经有缓存结果。如果是,则立即返回。如果不是,则计算结果并将其存储在缓存中。如果不这样做,它会一次又一次地重复,并且一旦达到序列中较高的数字,就会很快变得不可能缓慢。