浅谈自记忆函数
最近阅读《JavaScript忍者秘籍》看到了一种有趣的函数:自记忆函数。
简介
何为自记忆函数?书中提到:
记忆化(memoization)是一种构建函数的处理过程,能够记住上次计算结果
通过这句话可以得出,自记忆函数其实就是能够记住上次计算结果的函数。在实现中,我们可以这样进行处理:当函数计算得到结果时,就将该结果按照参数存储起来。采取这种方式时,如果另外一个调用也使用相同的参数,我们则可以直接返回上次存储的结果而不是再计算一遍。
显而易见,像这样避免既重复又复杂的计算可以显著提高性能。对于动画中的计算、搜索不经常变化的数据或任何耗时的数学计算来说,记忆化这种方式是十分有用的。
一个自记忆函数的例子
下面这个例子展现自记忆函数的工作方式:
// 自记忆素数检测函数
function isPrime (value) {
// 创建缓存
if (!isPrime.answers) {
isPrime.answers = {};
}
// 检查缓存的值
if (isPrime.answers[value] !== undefined) {
return isPrime.answers[value];
}
// 0和1不是素数
var prime = value !== 0 && value !== 1;
// 检查是否为素数
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
// 存储计算值
return isPrime.answers[value] = prime
}
isPrime
函数是一个自记忆素数检测函数,每当它被调用时:
首先,检查它的answers
属性来确认是否已经有自记忆的缓存,如果没有,创建一个。
接下来,检查参数之前是否已经被缓存过,如果在缓存中找到该值,直接返回缓存的结果。
如果参数是一个全新的值,进行正常的素数检测。
最后,存储并返回计算值。
总结
自记忆函数有两个优点:
- 由于函数调用时会寻找之前调用所得到的值,所以用户最终会乐于看到所获得的性能收益。
- 它不需要执行任何特殊请求,也不需要做任何额外初始化,就能顺利进行工作。
但是,自记忆函数并不是完美的,它一样有着缺陷:
- 任何类型的缓存都必然会为性能牺牲内存。
- 很多人认为缓存逻辑不应该和业务逻辑混合,函数或方法只需要把一件事情做好。
- 对自记忆函数很难做负载测试或估算算法复杂度,因为结果依赖于函数之前的输入。