如何在F#

时间:2015-10-23 14:29:03

标签: f# singleton memoization discriminated-union

我有一个Discriminated Union(“DU”)类型和一个属性OR方法,它根据DU实例计算某些东西。我试图实现一种模式,其中实例属性在第一次请求时执行计算,然后记住结果 - 类似于面向对象术语中的单例模式。

我发现这种挑战没有本地实例变量的帮助来存储事物的状态......

我尝试过对方法进行简单的memoization,但后来我遇到了没有(在实例中)存储memoized结果的问题。

注意:我的应用程序中会有很多DU类型的实例。

代码

// Terrible mutable variable needed alongside the DU to achieve a singleton-like pattern
let mutable result: int option = None

type DU = 
    | Good of int
    | Bad of int

with
    // behaves like a Singleton
    member this.GetSomething = 
        match result with
        | None ->        
            printfn "Big bad side-effect to let us know it's a first time"
            // big bad calculation that we only want to do once
            let x = 1 + 1
            // "memoize" it
            result <- Some x
            x
        | Some y -> y


let instance = Good 1
let f1 = instance.GetSomething  // first time
let f2 = instance.GetSomething  // second call - no side effect1

2 个答案:

答案 0 :(得分:5)

您无法记住不可变值的 ,因为记忆涉及更改和维护状态。

显然,你可以在外部中创建一个不可变的值:

let smth = getSomething "foo"

只要您重复使用smth而不是再次呼叫getSomething "foo",您就基本上会记住结果。如果getSomethingreferentially transparent,这是安全的。否则,它不是。

从OP中发布的示例代码中,您看起来更像是lazy initialization,您可以从Lazy<T> class获取。但是,您仍然需要一个对象来存储Lazy<T>实例。

open System

type MyObject() =
    let result =
        lazy
        printfn "Big bad side-effect to let us know it's a first time"
        // big bad calculation that we only want to do once
        1 + 1
    member this.GetSomething = result.Value

正如您所看到的,在F#中,您还可以使用lazy表达式。

> let mo = MyObject ();;    
val mo : MyObject

> let smth1 = mo.GetSomething;;
Big bad side-effect to let us know it's a first time    
val smth1 : int = 2

> let smth2 = mo.GetSomething;;    
val smth2 : int = 2

MyObject类可能看起来是不可变的,但这只是因为状态保存在lazy表达式中。

答案 1 :(得分:0)

这样就可以让DU内部变得懒惰。 因为类型是懒惰的。

type DU = 
    | Good of Lazy<int>  // Lazy not lazy
    | Bad  of Lazy<int>  

type MyObject() =
    let result =
        lazy(
        printfn "Big bad side-effect to let us know it's a first time"
        // big bad calculation that we only want to do once
        1 + 1) |> Good
    member this.GetSomething = result

let mo = MyObject()
let f() =
    match mo.GetSomething with
    | Good x -> x.Value
    | Bad  y -> y.Value

f()
f()

输出

Big bad side-effect to let us know it's a first time
val it : int = 2

> 
val it : int = 2