JS专题之memoization

栏目: JavaScript · 发布时间: 6年前

内容简介:在计算机领域,记忆(memoization)是主要用于加速程序计算的一种优化技术,它使得函数避免重复演算之前已被处理过的输入,而返回已缓存的结果。 -- wikipedia另外,

前言

在计算机领域,记忆(memoization)是主要用于加速程序计算的一种优化技术,它使得函数避免重复演算之前已被处理过的输入,而返回已缓存的结果。 -- wikipedia

Memoization 的原理就是把函数的每次执行结果都放入一个对象中,在接下来的执行中,在对象中查找是否已经有相应执行过的值,如果有,直接返回该值,没有才真正执行函数体的求值部分。在对象里找值是要比执行函数的速度要快的。

另外, Memoization 只适用于确定性算法,对于相同的输入总是生成相同的输出,即纯函数。

一、简单实现

通过 Memoization 的定义和原理,我们就可以初步实现 Memoization 了。

let memoize = function(func) {
  let cache = {};
  return function(key) {
    if (!cache[key])
      cache[key] = func.apply(this, arguments);
    return cache[key];
  }
}

是不是很简单~ 函数记忆其实就是利用闭包,将函数参数作为存储对象的键(key),函数结果作为存储对象的 value 值。

二、underscore 实现

underscore 的源码中有 Memoization 方法的封装,它支持传入一个 hasher 用来计算缓存对象 key 的计算方式。

_.memoize = function(func, hasher) {
  var memoize = function(key) {
    // 把存储对象的引用拿出来,便于后面代码使用
    var cache = memoize.cache;

    // hasher 是计算 key 值的方法函数。
    // 如果传入了 hasher,则用 hasher 函数来计算 key
    // 否则用 参数 key(即 memoize 方法传入的第一个参数)当 key
    var address = '' + (hasher ? hasher.apply(this, arguments) : key);

    // 如果 key 还没有对应的 hash 值(意味着没有缓存值,没有计算过该输入)
    // 就执行回调函数,并缓存结果值
    if (!_.has(cache, address))
      cache[address] = func.apply(this, arguments);

    // 从缓存对象中取结果值
    return cache[address];
  };

  // cache 对象被当做 key-value 键值对缓存中间运算结果
  memoize.cache = {};

  // 返回 momoize 函数, 由于返回函数内部引用了 memoize.cache, 构成了闭包,变量保存在了内存中。
  return memoize;
};

三、应用 - 判断素数

质数为在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数。

我们通过判断素数的函数,看看使用了函数记忆后的效果。

function isPrime(value) {
  console.log("isPrime 被执行了!");
  var prime = value != 1; // 1 不是素数,其他数字默认是素数。
  for (var i = 2; i < value; i++) {
    if (value % i == 0) {
      prime = false;
      break;
    }
  }
  return prime
}

let momoizedIsPrime = memoize(isPrime);

momoizedIsPrime(5) // isPrime 被执行了!
momoizedIsPrime(5) // 第二次执行,没有打印日志!

四、应用 - 计算斐波那契数列

指的是这样一个数列:1、1、2、3、5、8、13、21、……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2

计算斐波那契数列是用来演示函数记忆很好的例子,因为计算斐波那契数列函数里面用了大量的递归。

var count = 0;
var fibonacci = function(n) {
  count++;
  return n < 2 ? n : fibonacci(n - 2) + fibonacci(n - 1);
}

for(var i = 0; i<= 10; i++) {
    console.log(`i: ${i}, ` + fibonacci(i));
}

// i: 0, 0
// i: 1, 1
// i: 2, 1
// i: 3, 2
// i: 4, 3
// i: 5, 5
// i: 6, 8
// i: 7, 13
// i: 8, 21
// i: 9, 34
// i: 10, 55

console.log(count);  // 453 !!!

我们可以看出,如果从 0 开始打印斐波那契数列,fibonacci 函数被执行了 453 次。那我们就牺牲一小部分内存,用来缓存每次计算的值。

fibonacci = memoize(fibonacci);

for(var i = 0; i<= 10; i++) {
    console.log(`i: ${i}, ` + fibonacci(i));
}
console.log(count); // 12

通过 memoize 函数记忆,使得函数执行次数只需要 12 次,大大优化了函数执行计算的性能消耗,

总结

函数记忆(memoization)将函数的参数和结果值,保存在对象当中,用一部分的内存开销来提高程序计算的性能,常用在递归和重复运算较多的场景。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

XML Hacks

XML Hacks

Michael Fitzgerald / O'Reilly Media, Inc. / 2004-07-27 / USD 24.95

Developers and system administrators alike are uncovering the true power of XML, the Extensible Markup Language that enables data to be sent over the Internet from one computer platform to another or ......一起来看看 《XML Hacks》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具