我目前正在学习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所述,我可以使用列表理解来获得简单的列表。 最佳答案
我写了一个字符串生成器计算表达式here。
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
是可变的,但不一定必须是可变的)。无需更新现有数据,每个收益都会结合当前状态和传入的输入,从而生成一个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!
,因为我实际上不想在计算表达式中使用该值。