为什么memoize函数在D. Flanagan的“JS:Definitive Guide”需要arguments.length?

时间:2016-10-07 14:15:28

标签: javascript function arguments memoization higher-order-functions

我正在阅读David Flanagan的“Javascript:The Definitive Guide”。

在第8.8.4段中,他展示了一个高阶函数memoize(),它接受​​一个函数作为其参数,并返回该函数的记忆版本:

 //Return a memoized version of f.
// It only works if arguments to f all have distinct string representations.
 function memoize(f) {
      var cache = {}; // Value cache stored in the closure.

      return function() {
          // Create a string version of the arguments to use as a cache key.
          var key = arguments.length + Array.prototype.join.call(arguments,",");
          if (key in cache) return cache[key];
          else return cache[key] = f.apply(this, arguments);
      }
 }

在解释中有:“返回的函数将其arguments数组转换为字符串”。

如果我们只需要参数,为什么他将arguments.lengthArray.prototype.join.call(arguments, ",")连接起来而不是仅将arguments数组转换为字符串?

2 个答案:

答案 0 :(得分:4)

因为否则这两个调用将使用相同的密钥存储:

memoizedFunc('', '');
memoizedFunc(',');

在两种情况下,使用相同的字符串连接参数结果:,

答案 1 :(得分:1)

这个功能坏了。即使所有参数都是字符串,它也不起作用。见这个例子:



 //Return a memoized version of f.
// It only works if arguments to f all have distinct string representations.
 function memoize(f) {
      var cache = {}; // Value cache stored in the closure.

      return function() {
          // Create a string version of the arguments to use as a cache key.
          var key = arguments.length + Array.prototype.join.call(arguments,",");
          if (key in cache) return cache[key];
          else return cache[key] = f.apply(this, arguments);
      }
 }

const f = memoize(function(...args) {
  console.log('f was called')
  return args
})

console.log(f(',', ''))
console.log(f('', ','))




第二次使用不同的参数调用函数时,它不应该返回返回缓存的值。但是,'f was called'只会记录一次,因此无法按预期工作。

要创建一个适用于所有情况的函数,您必须将所有参数存储在缓存中,并迭代它们以检查它们是否相同。它可以这样实现:



const memoize = function(f) {
  const cache = []
  return (...args) => {
    for (const element of cache) {
      let hasSameArguments = true
      for (const i of args.keys()) {
        if (args[i] !== element.args[i]) {
          hasSameArguments = false
          break
        }
      }
      if (hasSameArguments) {
        return element.value
      }
    }
    const value = f(...args)
    cache.push({value, args})
    return value
  }
}

const f = memoize(function(...args) {
  console.log('f was called')
  return args
})

console.log(f(',', ''))
console.log(f('', ',')) // different arguments, f is called again

console.log(f(true))
console.log(f(true)) // from cache

const someObj = {}
     ,otherObj = {}

console.log(f(someObj))
console.log(f(someObj)) // the same object, result from cache
console.log(f(otherObj)) // different object, f is called again
console.log(f(otherObj))

console.log(f([1, 2, 3]))
console.log(f([1, 2, 3])) // different object, f is called again
                          // (because [1, 2, 3] !== [1, 2, 3])




请注意,它使用===运算符比较参数,因此,例如,如果使用包含相同值的数组调用该函数两次,则不会返回缓存的结果。您可以通过深度迭代参数并检查所有属性是否相同来更改此行为。