Javascript memoize函数可以使用ES6映射还是哈希表?

Javascript memoize函数可以使用ES6映射还是哈希表?,javascript,dictionary,ecmascript-6,hashtable,memoization,Javascript,Dictionary,Ecmascript 6,Hashtable,Memoization,前言:尝试通过转换利用哈希表的简单备忘录来学习 问题: 起始对象是否可以替换为新映射(),如果可以,如何替换?有什么好处吗?如果没有,原因是什么? 说明: 这是一个备忘录,它包含函数(add)和起始对象({})。调用记忆添加(mAdd)时,参数为。最后,测试/设置哈希索引并返回值 const memo=(fn,hash)=>(…a)=>{ 返回散列[a]==void 0?散列[a]=fn(…a):`${hash[a]}memo`; }; 常数加=(x,y)=>x+y; const mAdd

前言:尝试通过转换利用哈希表的简单备忘录来学习

问题: 起始对象是否可以替换为
新映射()
,如果可以,如何替换?有什么好处吗?如果没有,原因是什么?


说明:

这是一个备忘录,它包含函数(
add
)和起始对象(
{}
)。调用记忆添加(
mAdd
)时,参数为。最后,测试/设置哈希索引并返回值

const memo=(fn,hash)=>(…a)=>{
返回散列[a]==void 0?散列[a]=fn(…a):`${hash[a]}memo`;
};
常数加=(x,y)=>x+y;
const mAdd=memo(add,{});
console.log(mAdd(2,2));

console.log(mAdd(2,2))问题在于映射使用对象的引用来标识为键(如果提供的键是对象而不是基元类型)。在这种情况下,变量是一个数组,即使它在每次调用时具有相同的值,但该变量的引用不相同,因此Map将其理解为一个新键。看看下面的代码

const test=newmap();
常数a=['bla'];
测试集(a,'b');
console.log(test.get(a));
常数f=['bla'];

console.log(test.get(f))主要问题是参数不代表相同的对象。它们的内容确实如此,这就是为什么字符串化会起作用

使用对象作为散列,还执行一种字符串化:创建属性
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;
};
创建密钥有几种方法。字符串化是可能的,甚至可能
const key=uneval(a);
可以根据长度和内容创建某种类型的哈希整数,但其可靠性取决于可能的内容。例如,如果已知值从不超过100,且参数的数量不太长,则可以使用帮助器作为
const createKey=([a1,…a])=>a.length?a1+100*createKey(a):a1;
可以使用
const key=createKey(a);

当然,在本例中,直接添加总是比创建键和查找键快,但一般来说,创建键的方法是决定性因素

我意识到我在这方面可能没有说什么新的东西,但底线是传递的参数并不代表同一个对象。也就是说,我想建议另一个选项:创建一个分支映射。包含子映射(第一个参数为键)的基映射到结果(第二个参数为键)或后续映射到第二个元素

编辑所述分支的示例(单个映射可用于不同的功能以减少内存占用):

const memommap=new Map();//对所有已记忆函数的分支使用常规映射,因为结果作为键存储在子函数中
const memo=(fn)=>(…a)=>{
let key,r=a,map=memoMap;
while(r.length){
[key,…r]=r;
如果(地图有(键))
map=map.get(键);
其他的
set(key,map=newmap());
}
让res=map.get(fn);//获取此特定函数的结果
如果(res==未定义)
map.set(fn,res=fn(…a));
否则返回`${res}memo`;//x+y,
subtr=(x,y)=>x-y,
mAdd=备忘录(添加);
console.log(mAdd(2,2));
console.log(mAdd(2,2));
控制台日志(备忘录(子记录)(2,2));

console.log(memo(subtr)(2,2));
如果是,怎么做?
-将
{}
替换为
新映射()
@JaromandaX没那么容易?我也试过了,我也试过了-而且对我很有效,奇怪的是,你可能也想看看weakmap,你能不能为这个目的创建一个答案?我仍然不确定标记正确的问题是否是最好的答案。这是关键的创建,也是我缺少的.has方法!也谢谢你的参考!“对象不能用作地图中的键"他们可以。@zeroflagL你有更好的答案吗?我真的不喜欢用字符串化参数来创建key@zeroflagL是的。Matthew Harwood看一看我更新的帖子。@zeroflagL是,如果您不想通过字符串化参数来创建键,那么您可以用传入的对象的唯一属性替换对象本身,例如。{uid:“123”,name:“joe”}您可能会使用“123”。同时连接一组字符串以创建一个整体键(即使对于原语也是如此)的速度非常慢。请参阅使用常规JavaScript对象为缓存编制索引的备忘录的示例。