Dictionary 如何最好地仅基于参数而不是函数闭包,并在类内部进行记忆?

Dictionary 如何最好地仅基于参数而不是函数闭包,并在类内部进行记忆?,dictionary,reference,f#,heap,memoization,Dictionary,Reference,F#,Heap,Memoization,(编辑并重写问题以反映聊天讨论结果) 在一行中:给定一个状态单子中的一个状态,计算一次单子函数,缓存结果 我试图缓存函数求值的结果,其中缓存的键是状态单子的状态,并且我不关心可能的副作用:即,即使函数体在理论上可能发生变化,我知道它将独立于状态: f x = state { return DateTime.Now.AddMinutes(x) } g x = state { return DateTime.Now.AddMinutes(x) } 在这里,G10和F10应该产生相同的结果,它们可能

(编辑并重写问题以反映聊天讨论结果)

在一行中:给定一个状态单子中的一个状态,计算一次单子函数,缓存结果

我试图缓存函数求值的结果,其中缓存的键是状态单子的状态,并且我不关心可能的副作用:即,即使函数体在理论上可能发生变化,我知道它将独立于状态:

f x = state { return DateTime.Now.AddMinutes(x) }
g x = state { return DateTime.Now.AddMinutes(x) }
在这里,
G10
F10
应该产生相同的结果,它们可能不会因为双重调用
DateTime而有所不同。现在,它们必须是确定性的。为了便于论证,这里的变量状态是
x

同样地,
(g10)-(f5)
应该精确地产生
5分钟
,而不是多多少少一微秒


在发现缓存不起作用后,我使用with maps(或dict)将一个更复杂的解决方案调低到最低限度

记忆模式:

module Cache =
    let cache f = 
        let _cache = ref Map.empty
        fun x ->
        match (!_cache).TryFind(x) with
        | Some res -> res
        | None ->
             let res = f x
             _cache := (!_cache).Add(x,res)
             res
缓存应该作为计算生成器的一部分使用,在Run方法中:

type someBuilder() =
    member __.Run f = 
        Log.time "Calling __.Run"
        let memo_me =
            fun state ->
                let res = 
                    match f with
                    | State expr - expr state
                    | Value v -> state, v
                Log.time ("Cache miss, adding key: %A", s)
                res

        XCache.cache memo_me
这不起作用,因为由于闭包,每次缓存函数都不同,导致每次都命中缓存未命中。它应该独立于上面的
expr
,并且只依赖于
状态


我尝试将
\u缓存
放在模块级的缓存函数之外,但随后遇到了泛化问题:

值限制。已推断值“\u cache”具有泛型类型
将“_cache”定义为一个简单的数据项,使其成为具有显式参数的函数,或者,如果不希望它是泛型的,则添加一个类型注释

然后我尝试使用类型注释来解决这个问题,但由于同样的原因,我最终无法在泛型函数中使用它:它需要使用特定的类型注释:

let _cache<'T, 'U when 'T: comparison> ref : Map<'T, 'U>  = ref Map.empty
在FSI中运行示例的结果:


给我打电话
valit:(float*int)*字符串=((42.0,42),“测试我的值”)
给我打电话
valit:(float*int)*字符串=((42.0,42),“测试我的值”)


只需将缓存添加到运行中

member __.Run f = 
    printfn "Running!"

    Cache.cache <|

        fun s ->
        match f with
        | RS.Expression (State state) -> 
            printfn "Call me once!"
            state s
        | RS.Value v -> s, v
输出是

Call me once!
to cache
val it : (float * int) * string = ((42.0, 42), "test my value")

> 
from cache
val it : (float * int) * string = ((42.0, 42), "test my value")

按照您的设置方式,缓存不会存储在运行之间的任何位置。您需要在类中创建一个局部变量。@JohnPalmer:看来,无论我试图以何种方式转换它,我要么遇到了泛型问题(“表达式的类型应该是
obj
,但这里有
'a*'b
”,要么类似的说法是
(int*int)
不支持
IComparable
)。我听从了你的建议,但我找不到一种方法使它工作,似乎只要我尝试在模块级使用let语句来捕获可记忆的函数,泛型就被杀死了。与此类似,但不同的是:你能提供使用计算生成器的代码吗?@FyodorSoikin,我将尝试创建一个可管理且规模较大的示例。到目前为止,我的运行理论是,你实际上要对
Run
进行多次调用,而不是只进行一次调用,然后重复调用结果。但是让我们看看这个例子。谢谢你。看起来,
Cache.Cache实际上只创建了一个生成器,除非您多次调用
new builder()
,当然您不必这样做,否则您可以对所有计算使用相同的实例。
module Cache =
    let cache f = 
        let _cache = ref Map.empty
        fun x ->
            match (!_cache).TryFind(x) with
            | Some res -> printfn "from cache";  res
            | None ->
                 let res = f x
                 _cache := (!_cache).Add(x,res)
                 printfn "to cache"
                 res
Call me once!
to cache
val it : (float * int) * string = ((42.0, 42), "test my value")

> 
from cache
val it : (float * int) * string = ((42.0, 42), "test my value")