Какова роль циклов while в вычислительных выражениях F#?

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


person Markus Jarderot    schedule 02.01.2011    source источник


Ответы (1)


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

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

person kvb    schedule 02.01.2011
comment
Хороший. Я не знал о Run. - person Markus Jarderot; 02.01.2011