【问题标题】:Must memoized functions be defined as variables?必须将记忆函数定义为变量吗?
【发布时间】:2015-07-27 13:13:09
【问题描述】:

我刚刚问了这个问题 (multiple errors while momoizing function inside another function),我得到了一个很好的答案……但是!为了进一步了解 JavaScript,我想知道 momoized 函数是否可以用这种样式编写:

function main () {
    function memoized_f(){
        //memoizing code
    }
}

编辑:请注意,我不是在问上面的代码有什么区别,而是在问是否可以记住第二个代码!

那么,如何重写呢?

function main() {
  var create_node = (function() {
    var memo;
    console.log("memo: " + memo);
    console.log("create_node")

    function f() {
      var value;
      if (memo) {
        value = memo.cloneNode();
        console.log("clone node");
        console.log(value);
      } else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        console.log("new node");
        console.log("value: " + value);
        memo = value;
      }
      return value;
    }
    return f;
  })();
  var collection = [];
  for (var i = 0; i < 10; i++) {
    collection.push(create_node());
  };
  // Display results
  for (var i = 0; i < 10; i++) {
    console.log(i + ". " + collection[i]);
  }
}
main();

【问题讨论】:

  • 任何一个都可以,尽管它们并不完全相同。
  • ??这意味着什么?这两个样本实际上没有任何有意义的差异。在这两种情况下,都会有一个名为 memoized_f 的本地函数。
  • 所以你真的要问function x(){}var x = (function (){})()之间的区别?
  • 您似乎认为第一个例子可以记住,而第二个例子可能无法记住。我(和其他人)不明白为什么你会认为第二个例子不能被记住。如果您展示了如何记住第一个示例,并用它来解释您对第二个案例的担忧,也许会有所帮助。
  • @Vandervals 您的完整代码并未反映您的简单示例。您的第一个简单示例采用 var memoized_f = function(){ } 形式,但您的实际代码采用 var memoized_f = (function(){ })() 形式(其中立即调用的函数返回一个函数)。您可能希望澄清您的示例以更好地反映您的实际问题。

标签: javascript memoization


【解决方案1】:

由于 javascript 中的函数是一个对象,因此您可以只使用该函数来记忆值。我认为fib example 更有意义,但这是您的原始帖子。

function main() {
  // memoizing function
  function create_node() {
    var value;
    // read from memo on function object
    if (create_node.memo) {
      value = create_node.memo.cloneNode();
      value.innerHTML = 'cloned';
      console.log("clone node");
      console.log(value);
    } else {
      var value = document.createElement("div");
      value.innerHTML = "hello";
      console.log("new node");
      console.log("value: " + value);
      // retain memo on the function object
      create_node.memo = value;
    }
    return value;
  }

  var collection = [];
  for (var i = 0; i < 10; i++) {
    collection.push(create_node());
  };
  // Display results
  for (var i = 0; i < 10; i++) {
    console.log(i + ". " + collection[i]);
    document.getElementById('container').appendChild(collection[i]);
  }
}

main();
&lt;div id="container"&gt;&lt;/div&gt;

【讨论】:

  • 请注意,这将允许任何可以访问create_node 函数的代码访问和操作create_node.memo。 OP 原始文件中的闭包包装确保 memo 仅对记忆函数可见,其他任何地方都看不到。 (不过,如果这是你想要的,这个答案是一种非常有用的方法!目前还不清楚 OP 是否关心这个要求。)
  • 绝对正确,非常值得指出。我只是在回答如何使用函数本身重写的问题......显然,该模式与立即执行函数一起使用是有原因的(为了保持备忘录的私密性)。
  • 不能两者兼得吗?
  • 它是由立即执行的函数或调用返回记忆函数的“工厂”函数提供的闭包。问题是函数需要返回并存储在某个地方(例如在变量中)——所以你最终会再次得到类似于第一个模式的东西。
【解决方案2】:

你的实际记忆函数是f(function(){ ... })() IIFE wrapping 只是提供了一个额外的闭包层来隐藏变量memo,使其仅对f 可见。

重复一遍:(function(){...})() 表达式不是你的记忆函数。它限制了内部变量的可见性并最终返回你的记忆函数f,它是在它内部定义的。如果您可以将memo 暴露给main 中的其他代码并且不将其可见性限制为仅对记忆函数的可见性,则可以完全消除IIFE 包装,只需将f 重命名为create_node

function main() {
    var memo;

    function create_node() {
      var value;
      if (memo) { value = memo.cloneNode(); }
      else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        memo = value;
      }
      return value;
    }

  // use `create_node` as originally done
  // NOTE that other code can manipulate `memo` now, though!
}
main();

如果你愿意,你可以通过函数声明而不是 IIFE 来提供闭包包装:

function createMemoizedFunc() {
    var memo;

    function f() {
      var value;
      if (memo) { value = memo.cloneNode(); }
      else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        memo = value;
      }
      return value;
    }
    return f;
}
var create_node = createMemoizedFunc();

【讨论】:

  • 如果你执行这个createNode没有节点,它有函数f。如果我返回 f(),那么它总是执行 else 声明。
  • 不,在我的示例中,create_node 返回一个 div,否则该函数没有意义,它应该创建 div 或克隆它们
  • @Vandervals 是的,这个createNode(即createMemoizedFunc返回的函数)也返回了一个div;我不明白我的有什么不同。我已经检查过了,它只在第一次运行 else 块,就像你原来的那样。
  • 哦,我明白你了! createNode 是我必须用来创建节点的函数,而不是如何创建节点的示例!是的是的!但问题是我在问如何避免创建包含函数的变量......(如果有可能这样做的话)
  • @Vandervals 如果不将其分配给变量,您打算如何调用它?您可以消除标识符f 并返回一个匿名函数:而不是function f() { ... }; return f;,您只需执行return function() {...};。我不确定我是否完全理解“避免创建变量”的意思。
猜你喜欢
  • 2012-05-11
  • 2012-04-25
  • 2014-09-04
  • 2018-09-18
  • 2021-11-07
  • 1970-01-01
  • 2019-11-29
  • 1970-01-01
  • 2017-07-16
相关资源
最近更新 更多