如何按需计算一个值并将该值缓存在 FP/F# 中?

问题描述 投票:0回答:1

在 C# (OOP) 中,我可以轻松地按需计算值并记住计算后的值(也称为缓存),例如

    class WorkItem {
        private Dictionary<string, object> myFields;
        private WorkItemState myState;
        // ...
        public WorkItemState State {
            get {
                if (myState == null) {
                    myState = ParseWorkItemState(myFields["State"]);
                }
            }
        }
    }

或者我也可以使用

Lazy<T>

我可以使用类和可变字段在 F# 中模拟相同的 OOP 风格方法,或者 可能又是“懒惰”的表达方式,但更“惯用的 FP”方法是什么?

.net functional-programming f#
1个回答
0
投票

简短的回答是:与 C# 中的相同

状态

在函数式编程 (FP) 中,您始终可以使用 State Monad 来“模拟”状态突变,因此一种选择是传递状态:

let doSomething myFields myState =
    match myState with
    | Some s -> "foo", s
    | None -> "bar", parseWorkItemState (myFields["State"])

虽然“实用”,但人们可能会争论这是否真的有必要。

参考透明度

如果我们假设你想要记忆的状态是不可变的,并且计算它的函数是参照透明,那么是否记忆可变字段中的值就不那么重要了。

归根结底,这是一个关于 FP 到底是什么的半哲学问题。在我看来,这与引用透明度有关。如果您使用可变字段记忆引用透明函数,它仍然保持引用透明。 懒惰

如果您相信这个论点,那么您通常可以在 F# 中使用

lazy

计算,就像在 C# 中一样。然而,要求是记忆的函数是引用透明的。

如果不是,那么

你所做的一切都不会起作用

根据评论,我提出了
这个原始记忆片段

的以下变体: let memoize (dict : System.Collections.Generic.IDictionary<_, _>) fn x = match dict.TryGetValue x with | true, v -> v | false, _ -> let v = fn (x) dict.Add(x,v) v

它不是使用“全局”字典来存储记忆值,而是通过传入的字典来结束。人们可以通过部分应用它来创建记忆函数:

open System.Collections.Generic let f = memoize (Dictionary<int, int> ()) (fun n -> n*n)

记忆函数
f

关闭字典,它超出范围并与

f
本身一起被垃圾收集。
您可以传递 

f

,字典也随之而来。

这在概念上非常符合“闭包是对象,对象是闭包”的认识。

© www.soinside.com 2019 - 2024. All rights reserved.