JavaScript 使用函数实现“历史记录”

函数可以使用对象去记住先前操作的结果,从而避免多余的运算。

比如我们现在测试一个费波纳茨算法,我们可以使用递归函数计算fibonacci数列,一个fibonacci数字是之前两个fibonacci数字之和,最前面的两个数字是0和1。代码如下:

var count = 0;    //记录函数调用次数
 var fibonacci = function(n) {
    count++;
    return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
 };
 for(var i = 0; i <= 10; i+=1) {
    console.log(i+":"+fibonacci(i));
 }
 console.log(count); // 453

我们可以看到如上 fibonacci函数总共调用了453次,for循环了11次,它自身调用了442次,如果我们使用下面的记忆函数的话,那么就可以减少他们的运算次数,从而提高性能。

思路:先使用一个临时数组保存存储结果,当函数被调用的时候,先查找是否已经有存储结果,如果有的话,就立即返回这个存储结果,没有的话,调用函数进行运算。代码如下:

 1 var count2 = 0;
 2 var fibonacci2 = (function(){
 3     var memo = [0,1];
 4     var fib = function(n) {
 5         var result = memo[n];
 6         count2++;
 7         if(typeof result !== 'number') {
 8             result = fib(n-1) + fib(n-2);
 9             memo[n] = result;
10         }
11         return result;
12     };
13     return fib;
14  })();
15  for(var j = 0; j <= 10; j+=1) {
16     console.log(j+":"+fibonacci2(j));
17  }
18  console.log(count2); // 29

这个函数也返回了同样的结果,但是只调用了函数29次,循环了11次,也就是说函数自身调用了18次,从而减少无谓的函数的调用及运算,下面我们可以把这个函数进行抽象化,以构造带记忆功能的函数,如下代码:

 1 var count3 = 0;
 2 var memoizer = function(memo,formula) {
 3     var recur = function(n) {
 4         var result = memo[n];
 5         count3++;   // 这句代码只是说明运行函数多少次,在代码中并无作用,实际使用上可以删掉
 6         if(typeof result !== 'number') {
 7             result = formula(recur,n);
 8             memo[n] = result;
 9         }
10         return result;
11     };
12     return recur;
13  };
14  var fibonacci3 = memoizer([0,1],function(recur,n){
15     return recur(n-1) + recur(n-2);
16  });
17  // 调用方式如下
18  for(var k = 0; k <=10; k+=1) {
19     console.log(k+":"+fibonacci3(k));
20  }
21  console.log(count3); // 29

如上封装 memoizer 里面的参数是实现某个方法的计算公式,具体的可以根据需要自己手动更改,这里的思路无非就是想习惯使用对象去保存临时值,从而减少不必要的取值存储值的操作;

 

学习资源:理解使用函数实现历史记录--提高性能

原文地址:https://www.cnblogs.com/xiaochechang/p/5740837.html