【问题标题】:What is the role of `while`-loops in computation expressions in F#?`while`-loops 在 F# 计算表达式中的作用是什么?
【发布时间】:2011-01-02 03:59:28
【问题描述】:

如果您定义了构建器对象的While 方法,则可以在computation expressions 中使用while-loops。 While 方法的签名是:

member b.While (predicate:unit->bool, body:M<'a>) : M<'a>

为了比较,For 方法的签名是:

member b.For (items:seq<'a>, body:unit->M<'a>) : M<'a>

您应该注意到,在While 方法中,主体是一个简单类型,而不是For 方法中的函数。

您可以在计算表达式中嵌入一些其他语句,例如 let 和函数调用,但这些语句不可能在 while 循环中多次执行。

builder {
    while foo() do
      printfn "step"
      yield bar()
}

为什么while-loop 没有多次执行,而只是重复执行?为什么与 for 循环有显着差异?更好的是,是否有一些在计算表达式中使用 while 循环的预期策略?

【问题讨论】:

    标签: syntax f# computation-expression


    【解决方案1】:

    如果您查看how computation expressions are evaluated,您会看到

    while foo() do
      printfn "step"
      yield bar()
    

    被翻译成类似的东西

    builder.While(fun () -> foo(), 
                  builder.Delay(fun () -> 
                                  printfn "step"
                                  builder.Yield(bar()))))
    

    这种翻译允许对 while 循环的主体进行多次评估。虽然您的类型签名对于某些计算表达式(例如 seqasync)是准确的,但请注意,插入对 Delay 的调用可能会导致不同的签名。例如,您可以像这样定义一个列表构建器:

    type ListBuilder() =
      member x.Delay f = f
      member x.While(f, l) = if f() then l() @ (x.While(f, l)) else []
      member x.Yield(i) = [i]
      member x.Combine(l1,l2) = l1 @ l2()
      member x.Zero() = []
      member x.Run f = f()
    
    let list = ListBuilder()
    

    现在您可以评估如下表达式:

    list {
      let x = ref 0
      while !x < 10 do
        yield !x
        x := !x + 1
    }
    

    得到[0 .. 9]的等价物。

    这里,我们的While 方法具有签名(unit -&gt; bool) * (unit -&gt; 'a list) -&gt; 'a list,而不是(unit -&gt; bool) * 'a list -&gt; 'a list。一般来说,当Delay操作的类型为(unit -&gt; M&lt;'a&gt;) -&gt; D&lt;M&lt;'a&gt;&gt;时,While方法的签名将是(unit -&gt; bool) * D&lt;M&lt;'a&gt;&gt; -&gt; M&lt;'a&gt;

    【讨论】:

    • 不错。我不知道Run
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多