Wie implementiert man die Verzögerung im Builder für vielleichtes Berechnen?

8

Hier ist was ich bisher habe:

%Vor%

whileLoop funktioniert gut, um for loops zu unterstützen, aber ich sehe nicht, wie while loops unterstützt werden. Ein Teil des Problems ist, dass die Übersetzung von while-Schleifen delay verwendet, was ich in diesem Fall nicht herausfinden konnte. Die offensichtliche Implementierung ist wahrscheinlich falsch, da sie die Berechnung nicht verzögert, sondern stattdessen ausführt!

%Vor%

Die Verzögerung verhindert auch try...with und try...finally .

    
Joh 27.01.2012, 22:49
quelle

2 Antworten

10

Es gibt zwei verschiedene Möglichkeiten, Fortsetzungs-Builder in F # zu implementieren. Eine davon ist die Darstellung von verzögerten Berechnungen mit dem monadischen Typ (wenn eine Art der Darstellung von verzögerten Berechnungen unterstützt wird, wie Async<'T> oder der unit -> option<'T> Typ, wie durch kkm angezeigt.

Sie können jedoch auch die Flexibilität von F # -Berechnungsausdrücken verwenden und einen anderen Typ als Rückgabewert von Delay verwenden. Dann müssen Sie die Operation Combine entsprechend ändern und auch Run member implementieren, aber alles funktioniert ganz gut:

%Vor%

Der Trick ist, dass der F # -Compiler Delay verwendet, wenn Sie eine Berechnung haben, die verzögert werden muss - das heißt: 1) um die gesamte Berechnung zu umbrechen, 2) wenn Sie sequentiell Berechnungen erstellen, z. Verwenden von if in der Berechnung und 3) zum Verzögern von Körpern von while oder for .

In der obigen Definition gibt das Delay Mitglied unit -> M<'a> statt M<'a> zurück, aber das ist völlig in Ordnung, weil Combine und While als zweites Argument unit -> M<'a> nehmen. Darüber hinaus wird durch das Hinzufügen von Run , das die Funktion auswertet, das Ergebnis von maybe { .. } block (eine verzögerte Funktion) ausgewertet, da der gesamte Block an Run :

übergeben wird %Vor%

Dies ist eine Möglichkeit, den Berechnungsgenerator für nicht verzögerte Typen zu definieren, der höchstwahrscheinlich effizienter ist als der Wrapping-Typ innerhalb einer Funktion (wie in der kkm-Lösung) und keine spezielle verzögerte Version des Typs definieren muss.

Beachten Sie, dass dieses Problem z. Haskell, weil das eine faule Sprache ist, also müssen Berechnungen nicht explizit verzögert werden. Ich denke, dass die F # -Übersetzung sehr elegant ist, da sie sowohl verzögerte Typen ( Delay , die M<'a> zurückgeben) als auch Typen, die nur ein sofortiges Ergebnis darstellen, erlaubt (mit Delay , das eine Funktion & amp;% zurückgibt) co_de%).

    
Tomas Petricek 28.01.2012, 16:40
quelle
5

Laut monadischen Identitäten sollte Ihr delay immer

entsprechen %Vor%

Seit

%Vor%

Die delay hat die Signatur von

%Vor%

'T ist typgebunden an unit . Beachten Sie, dass die Argumente Ihrer bind -Funktion von der üblichen Reihenfolge bind p rest abweichen. Dies ist technisch gleich, aber komplizierter Lesecode.

Da Sie den monadischen Typ als type Maybe<'a> = option<'a> definieren, gibt es keine Verzögerung für eine Berechnung, da der Typ überhaupt keine Berechnungen, sondern nur einen Wert umschließt. Also ist die Definition der Verzögerung als let delay f = f() theoretisch korrekt. Für eine while-Schleife reicht es jedoch nicht aus: Der "Körper" der Schleife wird vor seiner "Testbedingung" berechnet, bevor die bind tatsächlich gebunden ist. Um dies zu vermeiden, definieren Sie Ihre Monade mit einer zusätzlichen Verzögerungsebene neu: Anstatt einen Wert zu umbrechen, wickeln Sie eine Berechnung um, die eine Einheit benötigt und den Wert berechnet.

%Vor%

Beachten Sie, dass die umbrochene Berechnung nicht innerhalb der bind -Funktion ausgeführt wird, d. e. nicht ausgeführt, bis die Argumente für bind selbst gebunden sind.

Mit dem obigen Ausdruck wird delay korrekt zu

vereinfacht %Vor%     
kkm 28.01.2012 02:19
quelle

Tags und Links