F#中函数之间的循环依赖

时间:2013-09-24 23:47:48

标签: recursion f# cyclic

我正试图在下面打破这种循环依赖,但不确定最好的方法。

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值,它仍能在几秒钟内运行。

1 个答案:

答案 0 :(得分:3)

问题实际上不在于你中间有moreComplicatedLogic(所以写大let rec不方便)。问题是您的代码以低效的方式实现Dynamic Programming algorithm

递归调用最终使用相同的参数多次调用cashClosingBalance(而不是只调用一次并将结果存储在某个本地缓存中)。在函数式编程中,您可以使用 memoization 的相当一般的概念来解决这个问题,但是您可能能够以不同的方式重写算法以使其更有效。

如果你想使用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是最后一个):< / p>

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)