2013-09-24 76 views
1

我試圖在下面打破這個循環依賴關係,但不確定最好的方法。F中函數之間的循環依賴關係#

let cashOpeningBalance t = 
if t = 1 then 
    0.0 
else 
    cashClosingBalance (t - 1) 

let cashInterest t = 
    cashOpeningBalance t * 0.03 

let accumulatedCash t = 
    cashOpeningBalance t + cashInterest t 

// let moreComplicatedLogic t = ... 

let cashClosingBalance t = 
    accumulatedCash t 

從這個answer使用一些邏輯,我想出了以下的解決方案,但表現真的很差。

let cashOpeningBalance t cashClosingBalance = 
if t = 1 then 
    10.0 
else 
    cashClosingBalance (t - 1) 

let cashInterest t cashClosingBalance = 
    (cashOpeningBalance t cashClosingBalance) * 0.03 

let accumulatedCash t cashClosingBalance = 
    (cashOpeningBalance t cashClosingBalance) + (cashInterest t cashClosingBalance) 

// let moreComplicatedLogic t cashClosingBalance = ... 

let rec cashClosingBalance t = 
    //accumulatedCash t cashClosingBalance 
    let temp = accumulatedCash t cashClosingBalance 
    printfn "Cash Closing Balance = %f Where t = %i" temp t 
    temp 

cashClosingBalance 3 
(* 
> 
Cash Closing Balance = 10.300000 Where t = 1 
Cash Closing Balance = 10.300000 Where t = 1 
Cash Closing Balance = 10.609000 Where t = 2 
Cash Closing Balance = 10.300000 Where t = 1 
Cash Closing Balance = 10.300000 Where t = 1 
Cash Closing Balance = 10.609000 Where t = 2 
Cash Closing Balance = 10.927270 Where t = 3 
val it : float = 10.92727 
*) 

cashClosingBalance 50 
(* 
Takes a really long time 
*) 

反正是有改寫cashClosingBalance功能停止過度的遞歸調用,如下面的輸出見過?我真的需要能夠提供高達400的t值,並且它仍然可以在幾秒鐘內運行。

回答

3

問題並不在於你的中間有moreComplicatedLogic(因此編寫大不方便)。問題在於你的代碼以低效的方式實現了Dynamic Programming algorithm

遞歸調用最終使用相同的參數多次調用cashClosingBalance(而不是隻調用一次並將結果存儲在本地緩存中)。在函數式編程中,您可以使用一個相當一般的概念memoization來解決這個問題,但是您可能會重寫您的算法以使其更有效。

如果你想使用記憶化,那麼你就需要這樣的事 - 以下助手接受一個函數,並且創建緩存以前調用的結果相同類型的函數:

let memoize f = 
    let dict = System.Collections.Generic.Dictionary<_, _>() 
    fun v -> 
    match dict.TryGetValue(v) with 
    | true, res -> res 
    | _ -> 
     let res = f v 
     dict.Add(v, res) 
     res 

然後你就可以重寫使用memoize這樣你的代碼(我只是包裹在memoize所有的功能定義和改變參數的順序,使t是最後一個):

let cashOpeningBalance cashClosingBalance = memoize (fun t -> 
    if t = 1 then 
    10.0 
    else 
    cashClosingBalance (t - 1)) 

let cashInterest cashClosingBalance = memoize (fun t -> 
    (cashOpeningBalance cashClosingBalance t) * 0.03) 

let accumulatedCash cashClosingBalance = memoize (fun t -> 
    (cashOpeningBalance cashClosingBalance t) + (cashInterest cashClosingBalance t)) 

// let moreComplicatedLogic t cashClosingBalance = ... 

let rec cashClosingBalance = memoize (fun t -> 
    //accumulatedCash t cashClosingBalance 
    let temp = accumulatedCash cashClosingBalance t 
    printfn "Cash Closing Balance = %f Where t = %i" temp t 
    temp) 
+0

感謝這個完美的作品。我喜歡memoize函數如何不改變程序的底層結構! – jeremyh

+0

以前沒見過這個設計策略。這似乎是一個動態創建查找表的巧妙方式。 *添加到工具箱* –