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>类型的属性,这很难使用)。

链接地址: http://www.djcxy.com/p/80543.html

上一篇: F#: Attempt to memoize member function resets cache on each call?

下一篇: Automatic memoizing in functional programming languages