为什么 D.Flanagan 的 "JS: Definitive Guide" 中的 memoize 功能需要 arguments.length?
Why does memoize function in D. Flanagan's "JS: Definitive Guide" need arguments.length?
我正在浏览 David Flanagan 的 "Javascript: The Definitive Guide"。
在第 8.8.4 段中,他展示了一个高阶函数,memoize()
接受一个函数作为其参数,returns 该函数的记忆版本:
//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);
}
}
解释中有:"The returned function converts its arguments array to a string"。
如果我们只需要参数,为什么他将 arguments.length
与 Array.prototype.join.call(arguments, ",")
连接起来,而不是仅将参数数组转换为字符串?
否则这两个调用将使用相同的密钥存储:
memoizedFunc('', '');
memoizedFunc(',');
在这两种情况下,连接参数的结果都是相同的字符串:,
此功能已损坏。即使所有参数都是字符串,它也不起作用。看这个例子:
//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('', ','))
第二次使用不同的参数调用该函数,因此它不应该 return return 缓存值。但是,'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])
请注意,它使用 ===
运算符比较参数,例如,如果您使用包含相同值的数组调用该函数两次,它不会 return 缓存结果。您可以通过深入迭代参数并检查所有属性是否相同来更改此行为。
我正在浏览 David Flanagan 的 "Javascript: The Definitive Guide"。
在第 8.8.4 段中,他展示了一个高阶函数,memoize()
接受一个函数作为其参数,returns 该函数的记忆版本:
//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);
}
}
解释中有:"The returned function converts its arguments array to a string"。
如果我们只需要参数,为什么他将 arguments.length
与 Array.prototype.join.call(arguments, ",")
连接起来,而不是仅将参数数组转换为字符串?
否则这两个调用将使用相同的密钥存储:
memoizedFunc('', '');
memoizedFunc(',');
在这两种情况下,连接参数的结果都是相同的字符串:,
此功能已损坏。即使所有参数都是字符串,它也不起作用。看这个例子:
//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('', ','))
第二次使用不同的参数调用该函数,因此它不应该 return return 缓存值。但是,'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])
请注意,它使用 ===
运算符比较参数,例如,如果您使用包含相同值的数组调用该函数两次,它不会 return 缓存结果。您可以通过深入迭代参数并检查所有属性是否相同来更改此行为。