F#中的计算表达式中,while循环的作用是什么?

8

如果你定义了建造者对象的While方法,那么你可以在你的计算表达式中使用while循环。 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循环不会执行多次,而只是重复执行?与for循环相比有何显著差异?更好的做法是,在计算表达式中使用while循环的一些策略吗?

1个回答

4
如果您查看计算表达式的求值方式,您会发现:
while foo() do
  printfn "step"
  yield bar()

被翻译成类似于以下内容

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

这种翻译允许 while 循环体被多次评估。虽然您的类型签名对于某些计算表达式(比如 seq 或者 async)是准确的,但请注意插入 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 -> bool) * (unit -> 'a list) -> 'a list,而不是(unit -> bool) * 'a list -> 'a list。一般来说,当Delay操作的类型为(unit -> M<'a>) -> D<M<'a>>时,While方法的签名将为(unit -> bool) * D<M<'a>> -> M<'a>


不错。我不知道 Run - Markus Jarderot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接