Какова роль циклов while в выражениях вычислений в F #? - PullRequest
7 голосов
/ 02 января 2011

Если вы определяете While метод объекта-строителя, вы можете использовать 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 не выполняется более одного раза, а просто повторяется? Почему существенное отличие от петель? А еще лучше, есть ли какая-то предполагаемая стратегия использования циклов while в выражениях вычислений?

1 Ответ

3 голосов
/ 02 января 2011

Если вы посмотрите на , как вычисляются вычислительные выражения , вы увидите, что

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>.

...