【问题标题】:Explanation on "JavaScript - the Good Parts" example (section 4.15)?关于“JavaScript - 好的部分”示例(第 4.15 节)的解释?
【发布时间】:2011-04-17 11:09:44
【问题描述】:

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)调用,那么它是如何详细工作的?

感谢您的帮助。

【问题讨论】:

  • 我希望这本书能够详细介绍该函数的工作原理——您有什么特别不明白的地方吗?
  • 嘿,我最近制作了一个关于使用 javascript 进行基本记忆的短视频 - 也许它有助于理解记忆器:youtube.com/watch?v=lsp82x0XdsY

标签: javascript memoization


【解决方案1】:

这是一个 console.log() 注释版本,它试图显示堆栈如何返回并将 (n-1)+(n-2) 的结果分配给每个相应递归调用的备忘录数组。还要记住堆栈以相反的顺序返回。所以在记录的输出中你会看到最后一个调用首先返回:

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);    
});

【讨论】:

    【解决方案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) 起作用的原因。

    【讨论】:

      【解决方案3】:

      正如您的问题所建议的那样,如果您无法按照书中的解释进行操作,您应该在调试器中遍历代码,以便很好地理解发生了什么。但我会简要概述一下正在发生的事情:

      演示的是“记忆化”,这是函数式编程中常用的优化技术。如果结果仅取决于传递给它的参数,则称该函数是纯函数。因此,如果一个函数是纯函数,您可以根据参数缓存结果——这种技术称为记忆化。如果一个函数的计算成本很高并且被多次调用,你会这样做。

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

      就这段代码而言,memoizer 有两个参数 - 'memo' 是缓存。在这种情况下,前两个值已经填写在“[0,1]”中 - 这些是前两个斐波那契数。

      第二个参数是要应用记忆的函数。在这种情况下,递归斐波那契函数:

      函数(外壳,n){ 返回壳(n-1)+壳(n-2); }

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

      memoizer 首先检查它是否已经有缓存结果。如果确实如此,它会立即返回。如果不是,它会计算结果并将其存储在缓存中。如果不这样做,它会一次又一次地重复自己,并且一次又一次地变得非常缓慢,以达到序列中更高的数字。

      【讨论】:

      • 感谢您的回答。然而我模糊的部分是不同的,但我自己已经得到了它:)...我想知道当fibonacii(15) 被调用时15 是如何传递到memoizer 函数的主体的。答案很简单:):memoizer 返回一个function(n)shell 变量),因此分配了function(n)memoizer 返回的var fibonacci 接受n=15
      • @MaxP。您的评论甚至比答案更有用,因为这也是我遇到的问题,特别是因为这本书没有提供调用语句1
      【解决方案4】:

      要评估函数,您只需调用它:

      fibonacci(15);
      

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

      alert(fibonacci(15));
      

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

      Console.log(fibonacci(15));
      

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

      fibonacci(15)
      

      【讨论】:

      • 感谢您的评论,我稍微修改了问题。我想听听为什么 fibonacci(15) 调用有效的详细解释? shell & n 操作对我来说很模糊......
      • 很确定他正在寻找某人来指导他了解代码的工作原理,使用 15 作为示例输入
      • 要详细了解它的工作原理,您可以再次下载 Firebug,然后使用调试器逐步调用 fibonacci(15)。寻找 Step Into 按钮。您可以添加一个监视表达式“n”来向您显示 n 的值随着它的变化而变化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      相关资源
      最近更新 更多