F#“有状态”计算表达式
我正在学习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()
]
链接地址: http://www.djcxy.com/p/24417.html
上一篇: F# "Stateful" Computation Expression
下一篇: F#: generated IL code for seq{} vs other computational workflows