Javascript 中的通用记忆方法

generic memoization approach in Javascript

我知道许多通用的记忆化方法依赖于对参数列表进行字符串化并将其用作键。例如。如:

Function.prototype.memoized = function() {
    this._values = this.values || {};
    var fn = this;
    return function() {
        var key = JSON.stringify( Array.prototype.slice.call(arguments) );
        if (fn._values[key]===undefined) {
            fn._values[key]=fn.apply(this, arguments);
        }
        return fn._values[key];
    };
};

当试图记忆 "member function" 时,这显然会失败,因为还必须 JSON 对上下文进行字符串化,即将其视为隐式传递的参数。但是,当上下文是全局对象或同样深的对象或以与函数本身无关的各种方式发生变化时,这将不会很好地工作。

但即使我们坚持使用非"member functions",也不一定总能完成对传递参数列表的严格控制,对吗?

三个问题:

  1. 我是否正确理解以通用方式记忆成员函数是无意义的?
  2. 我是否理解正确,以通用方式记忆非成员函数也是不可能的,因为无法/不切实际地完全字符串化任何可能的参数列表?
  3. 如果 2 成立,那么为什么那么多书籍和博客都试图在 Function.prototype 中定义通用 memoize 函数?有什么意义?

方法是 this 作为副作用来源的函数。正如您可能知道的那样,具有副作用的函数无法被记忆,因为这些影响不依赖于记忆所依赖的方法的参数列表。

但是当然有一个解决方法。我们可以手动指定方法所依赖的那些属性,而不是序列化整个对象(由 this 引用):

function memoize(f, deps) {
  let cache = {};

  return function(...args) {
    let key = JSON.stringify([deps(), args]), v;
    return cache[key] || (v = f.apply(this, args), cache[key] = v, v);
  };
}

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  this.fullName = memoize(
    function(title) { // memoized function
      console.log('memoizing...');
      return title + ' ' + this.firstName + ' ' + this.lastName;
    },
    function() { // dependencies
      return [this.firstName, this.lastName];
    }.bind(this));
}

let person = new Person('Jane', 'Doe');

// initial call
console.log(person.fullName('Ms.')); // memoizing...Ms. Jane Doe

// successive call
console.log(person.fullName('Ms.')); // Ms. Jane Doe

这只是一个概念验证,并不是一个经过全面优化和测试的解决方案。所有功劳都归功于 In Lehman's Terms


针对您的问题:

  1. 如果一种方法在计算方面非常昂贵,因此需要手动定义其隐式依赖项(this),那么不,它可能有用
  2. 是的,有时这是不可能的,但为什么要完全放弃几乎通用的解决方案?
  3. 不知道!旅鼠? :D