js-語言精粹-函數記憶


函數可以將先前操作的結果記錄在某個對象里,從而避免無謂的重復運算。這種優化方式被稱為記憶(memoization)。JavaScript的對象和數組要實現這種優化是非常方便的。

比如說,我們想要一個遞歸函數來計算Fibonacci數列。一個Fib數字是之前兩個Fib數字的和。最前面的兩個數字是0和1。

var count=0;
var fib=function(n){
    count++;
    return n<2?n:fib(n-1)+fib(n-2);
};
for(var i=0;i<=10;i++){
    console.log(fib(i))
}

//0
//1
//1
//2
//3
//5
//8
//13
//21
//34
//55

count
//453

這樣是可以工作的,但它做了很多無謂的工作,fib函數被調用了453次。如果我們讓該函數具備記憶功能,就可以顯著地減少運算量。

 

我們在一個名為memo的數組里保存我們的存儲結果,存儲結果可以隱藏在閉包中。當函數被調用時,這個函數首先檢查結果是否已經存在,如果已經存在,就立刻返回這個結果。

var count=0;
var fibonacci=function(){
    var memo=[0,1];
    var fib=function(n){
                count++;
        var result=memo[n];
        if(typeof result!=='number'){
            result=fib(n-1)+fib(n-2);
            memo[n]=result;
        }
        return result;
    };
    return fib;
}();

for(var i=0;i<=10;i++){
    console.log(fibonacci(i))
}
//結果同上

count
//29

這個函數返回同樣的結果,但它只被調用了29次。

 

我們可以把這種技術推而廣之,編寫一個函數來幫助我們構造帶記憶功能的函數。memoizer函數取得一個初始的memo數組和formula函數。它返回一個管理memo存儲和在需要時調用formula函數的recur函數。我們把這個recur函數和它的參數傳遞給formula函數:

var memoizer=function(memo,formula){
    var recur=function(n){
        var result=memo[n];
        if(typeof result!=='number'){
            result=formula(recur,n);
            memo[n]=result;
        }
        return result;
    };
    return recur;
};

現在我們可以使用memoizer函數來定義fibonacci函數,提供其初始的memo數組和formula函數:

var fibonacci=memoizer([0,1],function(recur,n){
    return recur(n-1)+recur(n-2);
});

通過設計這種產生另一個函數的函數,極大的減少了我們的工作量。例如,要產生一個可記憶的階乘函數,我們只需提供基本的階乘公式即可:

var factorial=memoizer([1,1],function(recur,n){
    return n*recur(n-1);
});

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM