JS专题之memoization

媒介

在盘算机范畴,影象(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)将函数的参数和结果值,保留在对象当中,用一部份的内存开支来进步顺序盘算的机能,常用在递归和反复运算较多的场景。

    原文作者:种瓜南山下
    原文地址: https://segmentfault.com/a/1190000018108579
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞