javascript - memoize 函数可以使用 ES6 Map 还是哈希表?

标签 javascript dictionary ecmascript-6 hashtable memoization

前言:努力学习ES6 Maps通过转换一个利用哈希表的简单 memoize。

问题:

是否可以用 new Map() 替换起始对象,如果可以,如何替换?有什么好处吗?如果不是,为什么?


描述:

这是一个采用函数 (add) 和起始对象 ({}) 的备忘录。在调用 memoized add (mAdd) 时,参数为 spread .最后,测试/设置哈希索引并返回值。

LINK TO CODE

const memo = (fn, hash) => (...a) => {
  return hash[a] === void 0 ?  hash[a] = fn(...a) : `${hash[a]} memo`;
};

const add = (x, y) =>  x + y;

const mAdd = memo(add, {});


console.log(mAdd(2,2));
console.log(mAdd(2,2));

不使用 map :

const memo = (fn, map) => (...a) => {
  return map.get(a) === void 0 ?  map.set(a, fn(...a)) : `${map.get(a)} memo`;
};

const add = (x, y) =>  x + y;

const mAdd = memo(add, new Map());


console.log(mAdd(2,2));
console.log(mAdd(2,2));

最佳答案

主要问题是参数不代表同一个对象。他们的内容确实如此,这就是为什么字符串化会起作用的原因。

使用对象作为散列,还执行一种字符串化:创建属性 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 返回 map 对象本身而不是正在设置的值)。

最简单的实现是将键字符串化。最安全的是 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;
};

可以通过多种方式创建 key 。 stringify 是可能的,甚至可能 const key = uneval(a); 可以根据长度和内容创建某种散列整数,但其可靠性取决于可能的内容。例如如果已知值永远不会超过 100 并且参数的数量不会太长,那么作为 const createKey = ([a1,...a]) => a.length 的助手? a1 + 100 * createKey(a) : a1; 可以用 const key =createKey(a);

调用

当然,对于示例,直接添加总是比创建 key 和查找 key 更快,但对于一般用途,创建 key 的方法是决定性因素。

我意识到我可能并不是在说所有这些新内容,但最重要的是传递的参数并不代表同一个对象。也就是说,我想建议另一种选择:创建一个分支 map 。包含子映射(以第一个参数为键)到结果(以第二个参数为键)或后续映射到第二个元素的基本映射。

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

const memoMap = new Map(); //use a general map for branches for all memoized functions, because result is stored in the child function as the key

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); //get result for this specific function
  if(res===undefined)
  	map.set(fn, res = fn(...a));
 	else return `${res} memo`; //<-- line for testing purposes
  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));

关于javascript - memoize 函数可以使用 ES6 Map 还是哈希表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40839290/

相关文章:

javascript - Jquery Ajax php - .one 函数无法正常工作

python - 在Python中合并多个列表中的字典

ecmascript-6 - 仅转换 JSX

当我使用 array.push 时,Javascript for of 变成 for of 重复值

javascript - jQuery animate() 在点击时控制不透明度

javascript - AJAX 超出最大调用堆栈大小

javascript - React如何更新n层以上父组件的状态

list - 如何映射代理集?

java - 随机 map /图表和 OSM

javascript - 检查对象值是否等于字符串值