F#:尝试记忆成员函数重置每次调用缓存?
我试图记忆一个类的成员函数,但每次调用该成员时(由另一个成员),它都会创建一个全新的缓存和“memoized”函数。
member x.internal_dec_rates =
let cache = new Dictionary< Basis*(DateTime option), float*float>()
fun (basis:Basis) (tl:DateTime option) ->
match cache.TryGetValue((basis,tl)) with
| true, (sgl_mux, sgl_lps) -> (sgl_mux, sgl_lps)
| _ ->
let (sgl_mux, sgl_lps) =
(* Bunch of stuff *)
cache.Add((basis,tl),(sgl_mux,sgl_lps))
sgl_mux,sgl_lps
我将“Real World Functional Programming”中的清单10.5用作模型。 我试过使用memoization高阶函数,但这没有帮助。 以上列表直接内置了备忘录。
问题是,当我称之为例如
member x.px (basis:Basis) (tl: DateTime option) =
let (q,l) = (x.internal_dec_rates basis tl)
let (q2,l2) = (x.internal_dec_rates basis tl)
(exp -q)*(1.-l)
执行进入'let cache = ...'行,击败了整个点。 为了确保它不是范围问题,我放入(q2,l2)行,但似乎并不是这样。
事实上,我使用Petricek的代码作为成员函数进行了测试,似乎也有同样的问题:
// Not a member function
let memo1 f =
let cache = new Dictionary<_,_>()
(fun x ->
match cache.TryGetValue(x) with
| true, v -> v
| _ -> let v = f x
cache.Add(x,v)
v
)
member x.factorial = memo1(fun y->
if (y<=0) then 1 else y*x.factorial(y-1))
即使是x.factorial的内部递归似乎也为每个级别设置了一个新的“缓存”。
我做错了什么,我该如何做这项工作?
回应你对杰克答案的评论,这不必变得单调乏味。 给定一个memoize函数:
let memoize f =
let cache = Dictionary()
fun x ->
match cache.TryGetValue(x) with
| true, v -> v
| _ ->
let v = f x
cache.Add(x, v)
v
将每个函数定义为let-bound值,并从您的方法中返回它们:
type T() as x =
let internalDecRates = memoize <| fun (basis: Basis, tl: DateTime option) ->
(* compute result *)
Unchecked.defaultof<float * float>
let px = memoize <| fun (basis, tl) ->
let (q,l) = x.InternalDecRates(basis, tl)
let (q2,l2) = x.InternalDecRates(basis, tl)
(exp -q)*(1.-l)
member x.InternalDecRates = internalDecRates
member x.Px = px
唯一的“样板”是let
绑定和调用memoize
。
编辑:正如kvb指出的,在F#3.0中,自动属性允许更简洁的解决方案:
type T() as x =
member val InternalDecRates = memoize <| fun (basis: Basis, tl: DateTime option) ->
(* compute result *)
Unchecked.defaultof<float * float>
member val Px = memoize <| fun (basis, tl) ->
let (q,l) = x.InternalDecRates(basis, tl)
let (q2,l2) = x.InternalDecRates(basis, tl)
(exp -q)*(1.-l)
我在这里看到很多很长的答案。 简单的答案是
member x.P = code()
定义了一个属性P
,它有一个每次访问P
都会运行code()
的getter。 您需要将缓存创建移动到该类的构造函数中,以便它只能运行一次。
正如其他人已经说过的那样,这不能通过在F#2.0中定义单个member
来完成。 您需要一个单独的字段( let
绑定值)为一个缓存或一个本地被记忆的函数。
正如kvb所提到的,在F#3.0中,可以使用member val
来完成此操作,该member val
是创建对象时初始化的属性(并且具有存储结果的自动生成的后备字段)。 这是一个完整的示例,演示了这一点(它将在Visual Studio 2012中工作):
open System.Collections.Generic
type Test() =
/// Property that is initialized when the object is created
/// and stores a function value 'int -> int'
member val Foo =
// Initialize cache and return a function value
let cache = Dictionary<int, int>()
fun arg ->
match cache.TryGetValue(arg) with
| true, res -> res
| false, _ ->
let res = arg * arg
printfn "calculating %d" arg
cache.Add(arg, res)
res
// Part of the property declaration that instructs
// the compiler to generate getter for the property
with get
声明的with get
部分可以省略,但是为了使示例更清晰(您也可以使用with get, set
来获取可变属性)。 现在,您可以调用test.Foo
作为函数,并根据需要缓存该值
let t = Test()
t.Foo(10)
t.Foo(10)
这种方法唯一的问题是t.Foo
实际上编译为一个返回函数的属性(而不是作为方法编译)。 当你使用F#中的类时,这不是一个大问题,但是如果你从C#调用它,会是一个问题(因为C#会将该成员看作FSharpFunc<int, int>
类型的属性,这很难使用)。
上一篇: F#: Attempt to memoize member function resets cache on each call?
下一篇: Automatic memoizing in functional programming languages