2012-04-16 41 views
4

任何人都可以給我一個underscore.js _.memoize()的例子嗎?實例中的underscore.js _.memoize()示例?

最好使用hashFunction,甚至更優選使用coffeescript?

這裏是一個可愛的改變計數功能從SICP在CoffeeScript中略加修改:

countChange = (amount)-> 

    cc = (amount, kindsOfCoins)-> 

    firstDenomination = (kindsOfCoins)-> 
     switch kindsOfCoins 
     when 1 then 1 
     when 2 then 5 
     when 3 then 10 
     when 4 then 25 

    if amount is 0 then 1 
    else if amount < 0 or kindsOfCoins is 0 then 0 
    else 
     (cc amount, (kindsOfCoins - 1)) + 
     (cc (amount - firstDenomination(kindsOfCoins)), kindsOfCoins) 

    cc amount*100, 4 


console.log "Ways to make change for $0.85: " + countChange(.85) 

我怎麼可能會使用下劃線的_.memoize()上的例子嗎?

非常感謝提前!

ps ..另外,請不要猶豫,以功能編碼的方式拍攝漏洞..我很新的咖啡標記和任何幫助使代碼更習慣性也歡迎。

回答

17

一個在這裏使用的memoize將是呼叫的數量減少到內cc功能:

n = 0 
countChange = (amount)-> 
    firstDenomination = (kindsOfCoins) -> 
    [1, 5, 10, 25][kindsOfCoins - 1] 

    cc = (amount, kindsOfCoins)-> 
    ++n # This is just a simple counter for demonstration purposes 
    return 1 if amount is 0 
    return 0 if amount < 0 or kindsOfCoins is 0 
    (cc amount, (kindsOfCoins - 1)) + 
     (cc (amount - firstDenomination(kindsOfCoins)), kindsOfCoins) 

    cc = _.memoize cc, (a,k) -> "#{a},#{k}" 

    cc amount*100, 4 

console.log "Ways to make change for $0.85: #{countChange(.85)}" 
​console.log "#{n} iterations of cc" 

我也重新安排事情緊湊了一點,我搬到外面firstDenominationcc簡化cc當我在那裏;不管我的firstDenomination是否比你的要好,我都有偏見,反對使用switch來實現簡單的查找表,但YMMV。

的memoized版本說 「211次迭代CC的」 演示:http://jsfiddle.net/ambiguous/FZsJU/

非memoized版本說 「CC的8141次迭代」,演示:http://jsfiddle.net/ambiguous/Xn944/

所以非memoized版本調用cc 〜40倍以上。根據散列函數的計算開銷(我的示例足夠用於演示目的,但未精確優化)和高速緩存查找的開銷,記憶可能有價值,也可能不值得。這是在記憶時要問的標準問題:緩存計算的緩存速度是否更快?

如果我們看一下_.memoize實現:

// Memoize an expensive function by storing its results. 
_.memoize = function(func, hasher) { 
    var memo = {}; 
    hasher || (hasher = _.identity); 
    return function() { 
    var key = hasher.apply(this, arguments); 
    return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 
    }; 
}; 

然後就可以看到它是如何工作的,以及如何使用hashermemo對象用作緩存,hasher用於將memoized函數的參數轉換爲memo中的鍵;如果我們找到了密鑰,那麼我們可以立即返回緩存的值,否則我們就會計算緩存的值(緩慢),並將其緩存並返回。

+0

哇!夢幻般的答案全線。非常感謝所有的細節和重組。非常有見地。 – James 2012-04-16 03:47:09

+0

快速跟進問:在hash函數中爲什麼你會返回這個:「#{a},#{k}」,而不是:[a,k] – James 2012-04-16 13:13:13

+0

@James:哈希函數必須返回可以使用的東西作爲「備忘錄」對象中的一個關鍵字,與使用'[a,k] .toString()'進行某種合理操作的瀏覽器相比,更好地明確轉換。 – 2012-04-16 17:16:13