遞歸操作是把問題逐漸縮小。
比如斐波那契數列,其遞歸公式是:
f(n) = f(n-1) + f(n-2)
f(0) = f(1) = 1
也就是說遞歸時,把問題規模減小時,可能會出現很多重復的子問題。f(0)和f(1)會被重復計算多次。
我們希望減少無用功,此時可以使用緩存來做。
例如下面的demo:
function f(n) { if (n < 2) return 1; return f(n - 1) + f(n - 2); } console.log(f(10));
輸出如下: console.log: 89;
可以使用緩存改成:
function f(n) { var cache = f.cache = f.cache || {}; if (n in cache) return cache[n]; if (n < 2) return cache[n] = 1; return cache[n] = f(n - 1) + f(n - 2); } console.log(f(10)); console.log(f.cache);
輸出如下:
console.log: 89 console.log: { 0: 1 1: 1 2: 2 3: 3 4: 5 5: 8 6: 13 7: 21 8: 34 9: 55 10: 89 }
使用函數記憶化:
代碼如下:
function memorize(func){ var fn = function(){ var cache = fn.cache; var key = [].join.call(arguments); if (!(key in cache)) return cache[key] = func.apply(this, arguments); return cache[key]; } fn.cache = {}; return fn; } var fn = memorize(function(n){ if (n < 2) return 1; return fn(n-1) + fn(n-2); }); console.log(fn(10)); // 89 console.log(fn.cache); //{0: 1, 1: 1, 2: 2, 3: 3, 4: 5, 5: 8, 6: 13, 7: 21, 8: 34, 9: 55, 10: 89}
memoize是對參數func進行了包裝,優先調用緩存。
緩存里沒有時,運行func,並把結果緩存起來。緩存對象的鍵是參數以逗號相連接的字符串。
比如,3個參數時sum(1, 2, 3),key會變成"1,2,3"。可以如下測試:
var sum = memorize(function(a,b,c){ return a + b + c; }) console.log(sum(1,2,3)); // 6 console.log(sum.cache); // {1,2,3: 6}
函數記憶化,是函數式編程中的概念。
函數記憶不一定只針對遞歸,但要求函數是純的。
即輸入確定,輸出就確定,不能對程序狀態搞下小動作。
因為內部利用了緩存,並不是每次都會運行函數。
