主要问题在于参数并不代表同一个对象,尽管它们的内容相同,这就是为什么字符串化会起作用的原因。
使用对象作为哈希时,也执行了一种串行化:创建了一个属性
2,2
。(顺便说一句: 这不是完整的证明,因为内容被扁平化了。参数
[1,[2,3]]
和
[1,2,3]
都将创建属性
[1,2,3]
)
然而,由于
Map
实际上更智能,对象本身被用作键,对于每次调用,新对象被创建来表示参数。
使用相同的参数进行调用将有效,但当然这会使函数变得不太有用:
var pars = [2,2]
console.log(mAdd(pars))
console.log(mAdd(pars))
(方法签名需要更改为
const memo = (fn, map) => (a) => {
才能使上述内容起作用。还请注意,
Map.set
返回的是映射对象本身,而不是正在设置的值。)
最简单的实现方法是将键序列化为字符串。最安全的方法是使用JSON.stringify
处理所有情况,但如果您相对确定内容,可以使用join
等方法:
const memo = (fn, map) => (...a) => {
const key = a.join(',');
if(map.has(key))
return `${map.get(key)} memo`;
let res = fn(...a);
map.set(key, res );
return res;
};
创建密钥有几种方式。可以使用stringify,甚至可以使用
const key = uneval(a);
。可以基于长度和内容创建某种哈希整数,但其可靠性取决于可能的内容。例如,如果已知值从不超过100且参数数量不是太长,则可以使用一个助手
const createKey = ([a1,...a]) => a.length ? a1 + 100 * createKey(a) : a1;
调用,并使用
const key =createKey(a);
。
当然,对于这个例子,直接添加总是比创建密钥和查找密钥更快,但对于一般情况,创建密钥的方法是决定性的因素。
我意识到在所有这些中,我可能没有说出新的东西,但最重要的是传递的参数不表示相同的对象。也就是说,我想提出另一种选择:创建分支映射。基本映射包含子映射(以第一个参数为键),指向结果(以第二个参数为键)或后续映射到第二个元素。
编辑 下面是所述分支的示例(单个映射可用于不同函数以减少内存占用):
const memoMap = new Map();
const memo = (fn) => (...a) => {
let key, r = a, map = memoMap;
while(r.length){
[key,...r] = r;
if(map.has(key))
map = map.get(key);
else
map.set(key, map = new Map());
}
let res = map.get(fn);
if(res===undefined)
map.set(fn, res = fn(...a));
else return `${res} memo`;
return res;
};
const add = (x, y) => x + y,
subtr = (x,y) => x - y,
mAdd = memo(add);
console.log(mAdd(2,2));
console.log(mAdd(2,2));
console.log(memo(subtr)(2,2));
console.log(memo(subtr)(2,2));
if so, how?
- replace{}
withnew Map()
- Jaromanda X