2011-01-02 4 views
7

Wenn Sie eine While Methode des Builder-Objekts definieren, können Sie while -loops in Ihrem computation expressions verwenden. Die Unterschrift der While Methode ist:Was ist die Rolle von while-Schleifen in Berechnungsausdrücken in F #?

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

Zum Vergleich wird die Unterzeichnung der For Methode ist:

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

Sie sollten feststellen, dass in dem While -Methode, der Körper ist eine einfache Art und nicht eine Funktion wie in der For Methode.

Sie können einige andere Anweisungen wie let und Funktionsaufrufe in Ihre Berechnungsausdrücke einbetten, aber diese können in einem while -loop mehr als einmal ausgeführt werden.

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

Warum ist die while -loop nicht mehr als einmal ausgeführt, sondern lediglich wiederholt? Warum der signifikante Unterschied zu For-Schleifen? Besser noch, gibt es eine bestimmte Strategie für die Verwendung von While-Schleifen in Berechnungsausdrücken?

Antwort

3

Wenn Sie bei how computation expressions are evaluated anschauen, werden Sie sehen, dass

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

etwas übersetzt wie

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

Diese Übersetzung ermöglicht es dem Körper der while-Schleife mehrfach ausgewertet werden. Während Ihre Typ-Signaturen für einige Berechnungsausdrücke korrekt sind (z. B. seq oder async), beachten Sie, dass das Einfügen des Aufrufs zu Delay zu einer anderen Signatur führen kann. Zum Beispiel könnten Sie eine Liste Builder wie folgt definieren:

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

Jetzt können Sie einen Ausdruck wie bewerten:

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

das Äquivalent von [0 .. 9] zu bekommen.

Hier hat unsere While Methode die Signatur (unit -> bool) * (unit -> 'a list) -> 'a list statt (unit -> bool) * 'a list -> 'a list. Wenn die Operation Delay den Typ (unit -> M<'a>) -> D<M<'a>> hat, ist die Signatur der Methode While(unit -> bool) * D<M<'a>> -> M<'a>.

+0

Schön. Ich wusste nichts über 'Run'. –