F#类内成员函数记忆化失效问题技术问询
F#类中记忆化失效问题的解决办法
你的问题根源在于类成员的定义方式:member this.MemoAddToOne = Memoize AddToOne是只读属性,每次访问该属性时都会重新执行Memoize AddToOne——也就是每次都会新建一个空的Dictionary,自然无法保留之前的缓存结果。而类外代码中Memoize仅被调用一次,对应的字典会持续存在,所以记忆化有效。
另外注意原代码里的fun c -是输入错误,正确写法是fun c ->。
修正方案1:将记忆化函数作为类字段初始化
把Memoize的调用放在类的字段定义中,这样类实例创建时仅执行一次Memoize,字典会绑定到实例上,持续保留缓存:
open System open System.Collections.Generic let Memoize f = let dict = Dictionary<_, _>() fun c -> let exists, value = dict.TryGetValue c match exists with | true -> value | _ -> let value = f c dict.Add(c, value) value type MyClass() as this = let rec AddToOne(x) = if x <= 1 then 1 else x + AddToOne(x-1) let rec AddToOneSkip(x) = if x <= 1 then 1 else x + AddToOneSkip(x-2) // 改为字段,实例化时仅执行一次Memoize let memoAddToOne = Memoize AddToOne let memoAddToOneSkip = Memoize AddToOneSkip // 成员属性返回预初始化的记忆化函数 member this.MemoAddToOne = memoAddToOne member this.MemoAddToOneSkip = memoAddToOneSkip [<EntryPoint>] let main args = let x = new MyClass() for i in 1..100000 do Console.WriteLine(x.MemoAddToOneSkip(i)) for i in 1..100000 do Console.WriteLine(x.MemoAddToOne(i)) 0
修正方案2:懒加载记忆化函数(可选)
如果担心实例化时就创建字典浪费资源,可以用lazy实现延迟初始化,第一次调用时才创建字典:
type MyClass() as this = let rec AddToOne(x) = if x <= 1 then 1 else x + AddToOne(x-1) let rec AddToOneSkip(x) = if x <= 1 then 1 else x + AddToOneSkip(x-2) // 懒加载,第一次访问时才执行Memoize let lazyMemoAddToOne = lazy (Memoize AddToOne) let lazyMemoAddToOneSkip = lazy (Memoize AddToOneSkip) member this.MemoAddToOne = lazyMemoAddToOne.Value member this.MemoAddToOneSkip = lazyMemoAddToOneSkip.Value
修改后,每个MyClass实例都会拥有独立的缓存字典,重复调用同一参数时会直接返回缓存值,不会重复计算。
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

