这可能不是规范的记忆功能。
需要缓存其结果的函数被赋予一个cache 函数,用于存储和检索以前的结果:
const sum = memo(cache => a => b => cache(`${a}+${b}`, () => a + b));
// ^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^
// A B C
-
A — cache 函数由 memo 函数提供。
(如果需要,memoized 函数可以选择不缓存某些结果。) em>
-
B — 结果的唯一键。 (例如cache['1+2'] = 3)
-
C — 返回结果的 thunk。
(因此我们可以在计算之前检查是否已经拥有它。)
这支持柯里化和非柯里化函数,也支持将函数作为值返回的函数。
memo函数可以实现如下:
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
我非常喜欢使用logical nullish assignment 操作符来管理缓存:
a ??= answer()
评估右侧的表达式并将其分配给a 当且仅当 a 尚未定义。然后返回a的值:
const answer = () => (console.log('returning the answer'), 42);
let a;
a ??= answer();
//=> LOG: returning the answer
//=> 42
a ??= answer();
//=> 42
a ??= 40;
//=> 42
我使用了一个符号来隐藏cache 函数上的实际缓存集。枚举对象属性时不返回符号:
const foo = {};
const key1 = Symbol();
const key2 = 'bar';
foo[key1] = 42;
foo[key2] = 41;
Object.keys(foo);
//=> ['bar']
Object.entries(foo);
//=> [['bar', 41]]
演示
// curried memoized function
const sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`computing ${a}+${b}…`), a+b)));
console.log(sum(1)(2));
console.log(sum(1)(2));
console.log(sum(1)(2));
// non-curried memoized function
const mul = memo(cache => (a, b) =>
cache(`${a}*${b}`,
() => (console.log(`computing ${a}*${b}…`), a*b)));
console.log(mul(2, 3));
console.log(mul(2, 3));
console.log(mul(2, 3));
// function-returning function
const deferred_sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`defer computing ${a}+${b}…`), () => a+b)));
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
<script>
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
</script>