Berechnungsausdrücke (F#)

Berechnungsausdrücke in F# stellen eine zweckmäßige Syntax zum Schreiben von Berechnungen bereit, die sequenziert und anhand von Ablaufsteuerungskonstrukten und Bindungen verbunden werden können. Sie können verwendet werden, um eine zweckmäßige Syntax für Monaden bereitzustellen, eine funktionale Programmierfunktion, die verwendet werden kann, um Daten-, Steuerelement- und Nebeneffekte in funktionalen Programmen zu verwalten.

Integrierte Workflows

Beispiele für Berechnungsausdrücke sind Sequenzausdrücke und asynchrone Workflows. Weitere Informationen zu Sequenzausdrücken finden Sie unter Sequenzen. Weitere Informationen zu asynchronen Workflows finden Sie unter Asynchrone Workflows.

Sequenzausdrücke und asynchrone Workflows weisen bestimmte gemeinsame Funktionen auf, die die grundlegende Syntax für einen Berechnungsausdruck veranschaulichen:

builder-name { expression }

Die vorherige Syntax gibt an, dass der angegebene Ausdruck ein Berechnungsausdruck eines von builder-name angegebenen Typs ist. Der Berechnungsausdruck kann ein integrierter Workflow sein, z. B. seq oder async oder benutzerdefiniert. Der builder-name ist der Bezeichner für eine Instanz eines speziellen Typs, der als Generatortyp bezeichnet wird. Der Generatortyp ist ein Klassentyp, der die speziellen Methoden für die Art und Weise, wie Fragmente des Berechnungsausdrucks kombiniert werden, definiert. Es handelt sich also um Code, der die Ausführung des Ausdrucks steuert. Eine Generatorklasse ermöglicht Ihnen, die Operation vieler F#-Konstrukte anzupassen, z. B. Schleifen und Bindungen.

In Berechnungsausdrücken sind zwei Formen für einige häufig verwendete Sprachkonstrukte verfügbar. Sie können die verschiedenen Konstrukte mit ! aufrufen. (Bang) hinter bestimmten Schlüsselwörtern aufgerufen, z. B. let!, do! usw. Aufgrund dieser speziellen Formen wird das gewöhnliche integrierte Verhalten dieser Vorgänge durch bestimmte in der Generatorklasse definierte Funktionen ersetzt. Diese Formen ähneln der yield!-Form des in Sequenzausdrücken verwendeten yield-Schlüsselworts. Weitere Informationen finden Sie unter Sequenzen.

Erstellen eines neuen Berechnungsausdruckstyps

Sie können die Merkmale eigener Berechnungsausdrücke definieren, indem Sie eine Generatorklasse erstellen und bestimmte spezielle Methoden für die Klasse definieren. Von der Generatorklasse können die Methoden optional definiert werden, wie in der folgenden Tabelle aufgeführt.

In der folgenden Tabelle werden Methoden beschrieben, die in einer Workflow-Generatorklasse verwendet werden können.

Methode

Typische Signatur

Beschreibung

Bind

M<'T> * ('T -> M<'U>) -> M<'U>

Für let! und do! in Berechnungsausdrücken aufgerufen.

Delay

(unit -> M<'T>) -> M<'T>

Fasst einen Berechnungsausdruck zu einer Funktion zusammen.

Return

'T -> M<'T>

Für return in Berechnungsausdrücken aufgerufen.

ReturnFrom

M<'T> -> M<'T>

Für return! in Berechnungsausdrücken aufgerufen.

Run

M<'T> -> M<'T> oder

M<'T> -> 'T

Führt einen Berechnungsausdruck aus.

Combine

M<'T> * M<'T> -> M<'T> oder

M<unit> * M<'T> -> M<'T>

Für die Sequenzierung in Berechnungsausdrücken aufgerufen.

For

seq<'T> * ('T -> M<'U>) -> M<'U>

seq<'T> * ('T -> M<'U>) -> seq<M<'U>>

Für for...do-Ausdrücke in Berechnungsausdrücken aufgerufen.

TryFinally

M<'T> * (unit -> unit) -> M<'T>

Für try...finally-Ausdrücke in Berechnungsausdrücken aufgerufen.

TryWith

M<'T> * (exn -> M<'T>) -> M<'T>

Für try...with-Ausdrücke in Berechnungsausdrücken aufgerufen.

Using

'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable

Für use-Bindungen in Berechnungsausdrücken aufgerufen.

While

(unit -> bool) * M<'T> -> M<'T>

Für while...do-Ausdrücke in Berechnungsausdrücken aufgerufen.

Yield

M<'T> -> M<'T>

Für yield-Ausdrücke in Berechnungsausdrücken aufgerufen.

YieldFrom

M<'T> -> M<'T>

Für yield!-Ausdrücke in Berechnungsausdrücken aufgerufen.

Zero

unit -> M<'T>

Für leere else-Verzweigungen von if...then-Ausdrücken in Berechnungsausdrücken aufgerufen.

Von vielen der Methoden in einer Workflow-Generatorklasse wird ein M<'T>-Konstrukt verwendet bzw. zurückgegeben, das normalerweise ein getrennt definierter Typ ist, der die Art der kombinierten Berechnungen charakterisiert, z B. Async<'T> für asynchrone und Seq<'T> für sequenzielle Workflows. Die Signaturen dieser Methoden ermöglichen ihre Kombination und Schachtelung, damit das von einem Konstrukt zurückgegebene Workflowobjekt an das Nächste übergeben werden kann. Wenn der Compiler einen Berechnungsausdruck analysiert, konvertiert er den Ausdruck mit den Methoden in der vorhergehenden Tabelle und dem Code im Berechnungsausdruck in eine Reihe geschachtelter Funktionsaufrufe.

Der geschachtelte Ausdruck hat folgende Form:

builder.Run(builder.Delay(fun () -> {| cexpr |}))

Im obigen Code werden die Aufrufe von Run und Delay weggelassen, wenn sie in der Generatorklasse des Berechnungsausdrucks nicht definiert sind. Der Text des Berechnungsausdrucks, hier als {| cexpr |} bezeichnet, wird anhand der in der folgenden Tabelle beschriebenen Übersetzungen in Aufrufe übersetzt, die die Methoden der Generatorklasse verwenden. Der Berechnungsausdruck {| cexpr |} ist entsprechend diesen Übersetzungen rekursiv definiert, wobei expr einen F#-Ausdruck und cexpr einen Berechnungsausdruck darstellt.

Ausdruck

Übersetzung

{| let binding in cexpr |}

let binding in {| cexpr |}

{| let! pattern = expr in cexpr |}

builder.Bind(expr, (fun pattern -> {| cexpr |}))

{| do! expr in cexpr |}

builder.Bind(expr1, (fun () -> {| cexpr |}))

{| yield expr |}

builder.Yield(expr)

{| yield! expr |}

builder.YieldFrom(expr)

{| return expr |}

builder.Return(expr)

{| return! expr |}

builder.ReturnFrom(expr)

{| use pattern = expr in cexpr |}

builder.Using(expr, (fun pattern -> {| cexpr |}))

{| use! value = expr in cexpr |}

builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {| cexpr |}))))

{| if expr then cexpr0 |}

if expr then {| cexpr0 |} else binder.Zero()

{| if expr then cexpr0 else cexpr1 |}

if expr then {| cexpr0 |} else {| cexpr1 |}

{| match expr with | pattern_i -> cexpr_i |}

match expr with | pattern_i -> {| cexpr_i |}

{| for pattern in expr do cexpr |}

builder.For(enumeration, (fun pattern -> {| cexpr }|))

{| for identifier = expr1 to expr2 do cexpr |}

builder.For(enumeration, (fun identifier -> {| cexpr }|))

{| while expr do cexpr |}

builder.While(fun () -> expr), builder.Delay({|cexpr |})

{| try cexpr with | pattern_i -> expr_i |}

builder.TryWith(builder.Delay({| cexpr |}), (fun value -> match value with | pattern_i -> expr_i | exn -> reraise exn)))

{| try cexpr finally expr |}

builder.TryFinally(builder.Delay( {| cexpr |}), (fun () -> expr))

{| cexpr1; cexpr2 |}

builder.Combine({|cexpr1 |}, {| cexpr2 |})

{| other-expr; cexpr |}

expr; {| cexpr |}

{| other-expr |}

expr; builder.Zero()

In der vorherigen Tabelle beschreibt other-expr einen Ausdruck, der sonst in der Tabelle nicht aufgeführt ist. Eine Generatorklasse muss nicht alle Methoden implementieren und nicht alle Übersetzungen unterstützen, die in der vorherigen Tabelle aufgeführt sind. Konstrukte, die nicht implementiert wurden, sind in Berechnungsausdrücken dieses Typs nicht verfügbar. Wenn Sie z. B. das use-Schlüsselwort in Berechnungsausdrücken nicht benötigen, können Sie die Definition von Use in der Generatorklasse weglassen.

Im folgenden Codebeispiel werden die Erstellung und Verwendung eines einfachen Berechnungsausdrucktyps veranschaulicht, der eine Konsolenausgabe erzeugt, die den Ausführungsstatus des Codes nachverfolgt.

// Program.fs
open Module1

// Create the computation expression object.
let trace1 = trace {
   // A normal let expression (does not call Bind).
   let x = 1
   // A let expression that uses the Bind method.
   let! y = 2
   let sum = x + y
   // return executes the Return method.
   return sum  
   }

// Execute the code. Start with the Delay method.
let result = trace1()

Im folgenden Code wird die Generatorklasse für den Verfolgungsberechnungsausdruck implementiert.

// Module1.fs
module Module1 =

 // Functions that implement the builder methods.
 let bind value1 function1 = 
     printfn "Binding %A." value1 
     function1 value1

 let result value1 =
     printfn "Returning result: %A" value1
     fun () -> value1

 let delay function1 =
     fun () -> function1()

 // The builder class for the "trace" workflow.
 type TraceBuilder() =
     member x.Bind(value1, function1) = 
         bind value1 function1
     member x.Return(value1)  = result value1
     member x.Delay(function1)   = 
         printfn "Starting traced execution."
         delay function1

 let trace = new TraceBuilder()

Die Ausgabe dieses Beispiels lautet wie folgt.

Starting traced execution.
Binding 2.
Returning result: 3

Siehe auch

Weitere Ressourcen

F#-Sprachreferenz

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

Dezember 2010

Run-Methode und Tabelle der Übersetzungen hinzugefügt.

Kundenfeedback.

April 2011

Yield hinzugefügt und Signatur für For in der Tabelle der Generatormethoden aktualisiert.

Korrektur inhaltlicher Fehler.