记住类型()的函数 - > '一个

时间:2013-12-12 16:29:01

标签: f# unit-type

This memoize function在运行时使用Null-Argument-Exception在() -> 'a类型的任何函数上失败。

let memoize f =
    let cache = System.Collections.Generic.Dictionary()
    fun x ->
        if cache.ContainsKey(x) then 
            cache.[x]
        else 
            let res = f x
            cache.[x] <- res
            res

有没有办法编写一个也适用于() -> 'a

的memoize函数

(我现在唯一的选择是使用Lazy类型。调用x.Force()来获取值。)

4 个答案:

答案 0 :(得分:11)

函数失败的原因是F#表示使用类型()的{​​{1}}的单位null。字典不允许将unit值作为键,因此失败。

在您的特定情况下,对null类型的函数进行记忆没有太大意义(因为最好使用unit -> 'a),但是在其他情况下这会是一个问题 - 例如lazy也由None表示,所以这也失败了:

null

解决此问题的简单方法是将密钥包装在另一种数据类型中,以确保它永远不会let f : int option -> int = memoize (fun a -> defaultArg a 42) f None

null

然后你可以用type Key<'K> = K of 'K 构造函数包装密钥,一切都会很好地工作:

K

答案 1 :(得分:5)

我刚发现使用Map而不是Dictionary的最后一个memoize函数on the same website也适用于'a Option -> 'b() -> 'a

let memoize1 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

答案 2 :(得分:2)

具有纯函数(不仅仅是类型unit -> 'a,而且还有任何其他函数)作为查找键的记忆是不可能的,因为函数通常没有reason的等式比较器。

对于这种特定类型的函数unit -> 'a,似乎可能会出现一个自定义的相等比较器。但实现超出极限(反射,IL等)的比较器的唯一方法是调用查找函数f1 = f2 iff f1() = f2(),这显然会使任何预期的性能改进无效。

所以,或许,正如已经指出的那样,对于这种情况,优化应围绕lazy模式构建,而不是memoization模式。

更新:事实上,在第二次查看问题之后,所有关于函数缺失等式的讨论都是正确的,但不适用,因为memoization发生在每个函数的闭包中的个体cache内。另一方面,对于具有签名unit->'a的这种特定类型的函数,即最多单个参数值,使用Dictionary与大多数一个条目是一种矫枉过正。以下类似的有状态但更简单的实现只有一个memoized值将执行:

let memoize2 f =
    let notFilled = ref true
    let cache = ref Unchecked.defaultof<'a>
    fun () ->
        if !notFilled then
            cache := f ()
            notFilled := false
        !cache

用作let foo = memoize2(fun () -> ...heavy on time and/or space calculation...) 首次使用foo()执行并存储计算结果,所有后续foo()只是重用存储的值。

答案 3 :(得分:-1)

使用可变字典和单字典查找调用的解决方案: let memoize1 f = // printfn "Dictionary" let cache = System.Collections.Generic.Dictionary() fun x -> let result, value = cache.TryGetValue(x) match result with | true -> value | false -> // printfn "f x" let res = f x cache.Add(x, res) res