簡體   English   中英

F#“​​有狀態”計算表達式

[英]F# “Stateful” Computation Expression

我正在學習F#並且遇到了一些絆腳石; 我認為很多是學習功能性思考。

我目前正在學習的一件事是計算表達式,我希望能夠定義一個處理某些跟蹤狀態的計算表達式,例如:

let myOptions = optionListBuilder {
    let! opt1 = {name="a";value=10}
    let! opt2 = {name="b";value=12}
}

我希望能夠擁有它,以便myOptions是一個Option<'T> list ,所以每個人都let! 綁定操作有效地使構建器在其進行時“跟蹤”定義的選項。

我不想使用可變狀態來執行此操作 - 例如,具有由構建器維護的列表並使用每個bind調用進行更新。

有沒有辦法讓它成為可能?


更新 :結果Option<'T> list類型只是代表性的,實際上我可能有一個OptionGroup<'T>類型來包含列表以及一些其他信息 - 所以Daniel在下面提到,我可以使用列表理解為一個簡單的列表。

在這里寫了一個字符串構建器計算表達

open System.Text

type StringBuilderUnion =
| Builder of StringBuilder
| StringItem of string

let build sb =
    sb.ToString()

type StringBuilderCE () =
    member __.Yield (txt : string) = StringItem(txt)
    member __.Yield (c : char) = StringItem(c.ToString())
    member __.Combine(f,g) = Builder(match f,g with
                                     | Builder(F),   Builder(G)   ->F.Append(G.ToString())
                                     | Builder(F),   StringItem(G)->F.Append(G)
                                     | StringItem(F),Builder(G)   ->G.Append(F)
                                     | StringItem(F),StringItem(G)->StringBuilder(F).Append(G))
    member __.Delay f = f()
    member __.Zero () = StringItem("")
    member __.For (xs : 'a seq, f : 'a -> StringBuilderUnion) =
                    let sb = StringBuilder()
                    for item in xs do
                        match f item with
                        | StringItem(s)-> sb.Append(s)|>ignore
                        | Builder(b)-> sb.Append(b.ToString())|>ignore
                    Builder(sb)

let builder1 = new StringBuilderCE ()

注意到底層類型是不可變的(包含的StringBuilder是可變的,但它不一定是)。 而不是更新現有數據,每個yield組合當前狀態和傳入輸入,從而產生StringBuilderUnion的新實例。您可以使用F#列表執行此操作,因為向列表頭部添加元素僅僅是構造新值而不是改變現有的價值觀。

使用StringBuilderCE如下所示:

//Create a function which builds a string from an list of bytes
let bytes2hex (bytes : byte []) =
    string {
        for byte in bytes -> sprintf "%02x" byte
    } |> build

//builds a string from four strings
string {
        yield "one"
        yield "two"
        yield "three"
        yield "four"
    } |> build

注意到yield而不是let! 因為我實際上並不想使用計算表達式中的值。

使用mydogisbox提供的基線StringBuilder CE構建器,我能夠生成以下具有魅力的解決方案:

type Option<'T> = {Name:string;Item:'T}

type OptionBuilderUnion<'T> =
    | OptionItems of Option<'T> list
    | OptionItem of Option<'T>

type OptionBuilder () =
    member this.Yield (opt: Option<'t>) = OptionItem(opt)
    member this.Yield (tup: string * 't) = OptionItem({Name=fst tup;Item=snd tup})
    member this.Combine (f,g) = 
        OptionItems(
            match f,g with
            | OptionItem(F), OptionItem(G) -> [F;G]
            | OptionItems(F), OptionItem(G) -> G :: F
            | OptionItem(F), OptionItems(G) -> F :: G
            | OptionItems(F), OptionItems(G) -> F @ G
        )
    member this.Delay f = f()
    member this.Run (f) = match f with |OptionItems items -> items |OptionItem item -> [item]

let options = OptionBuilder()

let opts = options {
        yield ("a",12)
        yield ("b",10)
        yield {Name = "k"; Item = 20}
    }

opts |> Dump

F#支持開箱即用的列表推導。

let myOptions =
    [
        yield computeOptionValue()
        yield computeOptionValue()
    ]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM