Zitieren von Code (F#)

In diesem Thema werden Codezitate beschrieben, die es Ihnen ermöglicht, programmgesteuert F#-Codeausdrücke zu generieren und mit diesen zu arbeiten. Mit dieser Funktion erstellen Sie eine abstrakte Syntaxstruktur, die F#-Code darstellt. Die abstrakte Syntaxstruktur kann dann durchlaufen und den Anforderungen der Anwendung entsprechend verarbeitet werden. Sie können z. B. die Struktur verwenden, um F#-Code zu generieren oder Code in einer anderen Sprache zu generieren.

Zitierte Ausdrücke

Ein zitierter Ausdruck ist ein F#-Ausdruck im Code, der so begrenzt wird, dass er nicht als Teil des Programms, sondern stattdessen in ein Objekt kompiliert wird, das einen F#-Ausdruck darstellt. Sie können einen zitierten Ausdruck auf zwei Weisen markieren: mit oder ohne Typinformationen. Wenn Sie Typinformationen einschließen möchten, grenzen Sie den zitierten Ausdruck mithilfe der Symbole <@ und @> ein. Wenn Sie keine Typinformationen benötigen, verwenden Sie die Symbole <@@ und @@>. Der folgende Code enthält typisierte und nicht typisierte Zitate.

open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>

Das Durchlaufen einer großen Ausdrucksbaumstruktur wird schneller ausgeführt, wenn Sie keine Typinformationen einschließen. Der resultierende Typ eines mit den Typisierungssymbolen zitierten Ausdrucks ist Expr<'T>, wobei der Typparameter den Typ des Ausdrucks aufweist, der vom üblichen Typrückschlussalgorithmus des F#-Compilers bestimmt wird. Wenn Codezitate ohne Typinformationen verwendet werden, ist der Typ des zitierten Ausdrucks der nicht generische Typ Expr. Sie können die Raw-Eigenschaft für die typisierte Expr-Klasse aufrufen, um das nicht typisierte Expr-Objekt abzurufen.

Es gibt eine Vielzahl von statischen Methoden, die Ihnen ermöglichen, F#-Ausdrucksobjekte programmgesteuert in der Expr-Klasse ohne zitierte Ausdrücke zu erstellen.

Beachten Sie, dass ein Codezitat einen vollständigen Ausdruck enthalten muss. Für eine let-Bindung benötigen Sie beide Definitionen des gebundenen Namens und einen zusätzlichen Ausdruck, der zur Bindung verwendet wird. In ausführlicher Syntax ist dies ein Ausdruck, der dem in-Schlüsselwort folgt. Auf der obersten Ebene in einem Modul ist dies nur der nächste Ausdruck im Modul, in einem Zitat ist er explizit erforderlich.

Daher ist der folgende Ausdruck nicht gültig.

// Not valid:
// <@ let f x = x + 1 @>

Die folgenden Ausdrücke sind jedoch gültig.

// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@ 
    let f x = x + 10
    f 20
@>

Zum Verwenden von Codezitaten müssen Sie (mit dem open-Schlüsselwort) eine Importdeklaration hinzufügen, die den Microsoft.FSharp.Quotations-Namespace öffnet.

Der F#-PowerPack bietet Unterstützung für das Auswerten und Ausführen von F#-Ausdrucksobjekten.

Expr-Typ

Eine Instanz des Expr-Typs stellt einen F#-Ausdruck dar. Der generische und der nicht generische Expr-Typ sind in der Dokumentation zur F#-Bibliothek enthalten. Weitere Informationen finden Sie unter Microsoft.FSharp.Quotations-Namespace (F#) und unter Quotations.Expr-Klasse (F#).

Zusammenführen von Operatoren

Das Zusammenführen ermöglicht Ihnen, literale Codezitate mit Ausdrücken, die Sie programmgesteuert oder aus einem anderen Codezitat erstellt haben, zu kombinieren. Die %- und %%-Operatoren ermöglichen Ihnen, ein F#-Ausdrucksobjekt einem Codezitat hinzuzufügen. Mithilfe des %-Operators fügen Sie ein typisiertes Ausdrucksobjekt in ein typisiertes Zitat ein. Mithilfe des %%-Operators fügen Sie ein nicht typisiertes Ausdrucksobjekt in ein nicht typisiertes Zitat ein. Beide Operatoren sind unäre Präfixoperatoren. Daher ist der folgende Code gültig, wenn expr ein nicht typisierter Ausdruck des Expr-Typs ist.

<@@ 1 + %%expr @@>

Wenn expr ein typisiertes Zitat des Expr<int>-Typs ist, ist der folgende Code gültig.

<@ 1 + %expr @>

Beispiel

Beschreibung

Im folgenden Beispiel wird die Verwendung von Codezitaten zum Setzen von F#-Code in ein Ausdrucksobjekt und dem anschließenden Drucken von F#-Code, der den Ausdruck darstellt, dargestellt. Eine println-Funktion wird definiert, die eine rekursive print-Funktion enthält, die ein F#-Ausdrucksobjekt (vom Typ Expr) in einem benutzerfreundlichen Format anzeigt. Es gibt mehrere aktive Muster in den Microsoft.FSharp.Quotations.Patterns- und Microsoft.FSharp.Quotations.DerivedPatterns-Modulen, die zum Analysieren von Ausdrucksobjekten verwendet werden können. Dieses Beispiel enthält nicht alle in einem F#-Ausdruck anzeigbaren möglichen Muster. Jedes nicht erkannte Muster löst eine Übereinstimmung mit dem Platzhaltermuster aus und (_) wird mit der ToString-Methode gerendert, mit der Sie für den Expr-Typ das aktive Muster ermitteln können, das dem Vergleichsausdruck hinzugefügt werden soll.

Code

module Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let println expr =
    let rec print expr =
        match expr with
        | Application(expr1, expr2) ->
            // Function application.
            print expr1
            printf " "
            print expr2
        | SpecificCall <@@ (+) @@> (_, _, exprList) ->
            // Matches a call to (+). Must appear before Call pattern.
            print exprList.Head
            printf " + "
            print exprList.Tail.Head
        | Call(exprOpt, methodInfo, exprList) ->
            // Method or module function call.
            match exprOpt with
            | Some expr -> print expr
            | None -> printf "%s" methodInfo.DeclaringType.Name
            printf ".%s(" methodInfo.Name
            if (exprList.IsEmpty) then printf ")" else
            print exprList.Head
            for expr in exprList.Tail do
                printf ","
                print expr
            printf ")"
        | Int32(n) ->
            printf "%d" n
        | Lambda(param, body) ->
            // Lambda expression.
            printf "fun (%s:%s) -> " param.Name (param.Type.ToString())
            print body
        | Let(var, expr1, expr2) ->
            // Let binding.
            if (var.IsMutable) then
                printf "let mutable %s = " var.Name
            else
                printf "let %s = " var.Name
            print expr1
            printf " in "
            print expr2
        | PropertyGet(_, propOrValInfo, _) ->
            printf "%s" propOrValInfo.Name
        | String(str) ->
            printf "%s" str
        | Value(value, typ) ->
            printf "%s" (value.ToString())
        | Var(var) ->
            printf "%s" var.Name
        | _ -> printf "%s" (expr.ToString())
    print expr
    printfn ""


let a = 2

// exprLambda has type "(int -> int)".
let exprLambda = <@ fun x -> x + 1 @>
// exprCall has type unit.
let exprCall = <@ a + 1 @>

println exprLambda
println exprCall
println <@@ let f x = x + 10 in f 10 @@>

Output

fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10

Beispiel

Beschreibung

Sie können auch die drei aktiven Muster im ExprShape-Modul verwenden, um Ausdrucksbaumstrukturen mit weniger aktiven Mustern zu durchlaufen. Diese aktiven Muster können beim Durchlaufen einer Struktur nützlich sein, aber in den meisten Knoten werden die Informationen nicht benötigt. Wenn Sie diese Muster verwenden, wird jedem F#-Ausdruck das entsprechende Muster zugewiesen: ShapeVar, wenn der Ausdruck eine Variable ist, ShapeLambda, wenn der Ausdruck ein Lambda-Ausdruck ist, oder ShapeCombination, wenn der Ausdruck ein anderes Element ist. Wenn Sie mit den aktiven Mustern wie im vorherigen Codebeispiel eine Ausdrucksbaumstruktur durchlaufen, sind zur Behandlung aller möglichen F#-Ausdruckstypen viel mehr Muster erforderlich, und der Code ist komplexer. Weitere Informationen finden Sie unter Aktives Muster ExprShape.ShapeVar|ShapeLambda|ShapeCombination (F#).

Das folgende Codebeispiel kann als Grundlage für komplexere Durchläufe verwendet werden. In diesem Code wird eine Ausdrucksbaumstruktur für einen Ausdruck erstellt, der einen Funktionsaufruf, add, einschließt. Das aktive Muster SpecificCall wird verwendet, um alle Aufrufe von add in der Ausdrucksbaumstruktur zu ermitteln. Dieses aktive Muster weist die Argumente des Aufrufs dem exprList-Wert zu. In diesem Fall sind nur zwei vorhanden, deshalb werden diese herausgezogen, und die Funktion wird rekursiv für die Argumente aufgerufen. Die Ergebnisse werden in ein Codezitat eingefügt, das einen Aufruf von mul mithilfe des Operators zum Zusammenführen (%%) darstellt. Die println-Funktion vom vorherigen Beispiel wird verwendet, um die Ergebnisse anzuzeigen.

Der Code in den anderen aktiven Musterverzweigungen generiert lediglich die gleiche Ausdrucksbaumstruktur neu. Deshalb ist die einzige Änderung im resultierenden Ausdruck die Änderung von add zu mul.

Code

module Module1
open Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape

let add x y = x + y
let mul x y = x * y

let rec substituteExpr expression =
    match expression with
    | SpecificCall <@@ add @@> (_, _, exprList) ->
        let lhs = substituteExpr exprList.Head
        let rhs = substituteExpr exprList.Tail.Head
        <@@ mul %%lhs %%rhs @@>
    | ShapeVar var -> Expr.Var var
    | ShapeLambda (var, expr) -> Expr.Lambda (var, substituteExpr expr)
    | ShapeCombination(shapeComboObject, exprList) ->
        RebuildShapeCombination(shapeComboObject, List.map substituteExpr exprList)

let expr1 = <@@ 1 + (add 2 (add 3 4)) @@>
println expr1
let expr2 = substituteExpr expr1
println expr2

Output

1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))

Siehe auch

Weitere Ressourcen

F#-Sprachreferenz

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

Mai 2010

Letztes Codebeispiel korrigiert.

Korrektur inhaltlicher Fehler.