2011-01-02 17 views
7

Jeśli zdefiniujesz metodę While obiektu-budowniczego, możesz użyć while -ups w swoim computation expressions. Podpis metody While jest:Jaka jest rola `while`-loops w wyrażeniach obliczeniowych w F #?

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

Dla porównania, podpis metody For jest:

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

Należy zauważyć, że w While -method, ciało jest prosty typ , a nie funkcja jak w metodzie For.

Można osadzić kilka innych instrukcji, takich jak let i wywołań funkcji wewnątrz wyrażeń obliczeniowych, ale te mogą niemożliwie wykonać w while -więcej niż jeden raz.

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

Dlaczego while nie -loop wykonywane więcej niż jeden raz, a jedynie powtórzył? Dlaczego znacząca różnica w stosunku do pętli for? Co więcej, czy jest jakaś strategia wykorzystania pętli while w wyrażeniach obliczeniowych?

Odpowiedz

3

Jeśli spojrzeć na how computation expressions are evaluated, zobaczysz, że

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

jest tłumaczony na coś jak

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

To tłumaczenie pozwala ciało pętli while być oceniane wiele razy. O ile podpisy typów są poprawne dla niektórych wyrażeń obliczeniowych (takich jak seq lub async), należy pamiętać, że wstawienie połączenia do Delay może spowodować powstanie innej sygnatury. Na przykład, można zdefiniować listę Builder tak:

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() 

Teraz można ocenić wyrażeń takich jak:

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

dostać równowartość [0 .. 9].

Tutaj nasza metoda ma podpis (unit -> bool) * (unit -> 'a list) -> 'a list, a nie (unit -> bool) * 'a list -> 'a list. Ogólnie rzecz biorąc, gdy operacja Delay ma typ (unit -> M<'a>) -> D<M<'a>>, sygnatura metody będzie oznaczona .

+0

Nice. Nie wiedziałem o "Run". –