Uvozovky kódu

Tento článek popisuje uvozovky kódu, což je funkce jazyka, která umožňuje programově generovat a pracovat s výrazy kódu jazyka F#. Tato funkce umožňuje vygenerovat abstraktní strom syntaxe, který představuje kód jazyka F#. Abstraktní strom syntaxe se pak dá projít a zpracovat podle potřeb vaší aplikace. Pomocí stromu můžete například vygenerovat kód jazyka F# nebo vygenerovat kód v jiném jazyce.

Citované výrazy

Výraz v uvozování je výraz F# v kódu, který je oddělený takovým způsobem, že není zkompilován jako součást programu, ale je zkompilován do objektu, který představuje výraz F#. Výraz v uvozovky můžete označit jedním ze dvou způsobů: buď pomocí informací o typu, nebo bez informací o typu. Pokud chcete zahrnout informace o typu, použijete symboly <@ a @> oddělíte uvozový výraz. Pokud nepotřebujete psát informace, použijte symboly <@@ a @@>. Následující kód ukazuje typované a nezatypované uvozovky.

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

Procházení velkého stromu výrazů je rychlejší, pokud nezadáte informace o typu. Výsledný typ výrazu citovaného se zadanými symboly je Expr<'T>, kde parametr typu má typ výrazu určený algoritmem odvozování typu kompilátoru jazyka F#. Při použití uvozovek s kódem bez informací o typu je typ citovaného výrazu ne generickým typem Výraz. Můžete volat Raw vlastnost typed Expr třídy získat netypový Expr objekt.

Existují různé statické metody, které umožňují generovat objekty výrazu F# programově ve Expr třídě bez použití uvozových výrazů.

Uvozovky kódu musí obsahovat úplný výraz. let Například pro vazbu potřebujete definici vázaného názvu i jiného výrazu, který používá vazbu. Ve podrobné syntaxi se jedná o výraz, který následuje za klíčovým slovem in . Na nejvyšší úrovni v modulu je to jen další výraz v modulu, ale v uvozovkách se explicitně vyžaduje.

Proto následující výraz není platný.

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

Následující výrazy jsou ale platné.

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

K vyhodnocení uvozovek jazyka F# je nutné použít vyhodnocovače uvozovek jazyka F#. Poskytuje podporu pro vyhodnocování a spouštění objektů výrazů jazyka F#.

Uvozovky jazyka F# také uchovávají informace o omezení typu. Představte si následující příklad:

open FSharp.Linq.RuntimeHelpers

let eval q = LeafExpressionConverter.EvaluateQuotation q

let inline negate x = -x
// val inline negate: x: ^a ->  ^a when  ^a : (static member ( ~- ) :  ^a ->  ^a)

<@ negate 1.0 @>  |> eval

Omezení vygenerované inline funkcí je zachováno v uvozovkách kódu. Formulář negate citované funkce se teď dá vyhodnotit.

Typ výrazu

Instance Expr typu představuje výraz jazyka F#. Obecné i ne generické Expr typy jsou popsané v dokumentaci k knihovně F#. Další informace naleznete v tématu FSharp.Quotations Namespace and Quotations.Expr – třída.

Operátory splicingem

Splicing umožňuje kombinovat literální uvozovky kódu s výrazy, které jste vytvořili programově nebo z jiné uvozovky kódu. %% Operátory % umožňují přidat objekt výrazu F# do uvozovek kódu. Pomocí operátoru % vložíte objekt zadaného výrazu do typové uvozovky. Pomocí operátoru %% vložíte netypový objekt výrazu do nezatypované uvozovky. Oba operátory jsou unární operátory předpon. expr Pokud je tedy netypový výraz typu Expr, je následující kód platný.

<@@ 1 + %%expr @@>

A pokud expr se jedná o typovou uvozovku typu Expr<int>, je platný následující kód.

<@ 1 + %expr @>

Příklad 1

Popis

Následující příklad znázorňuje použití uvozovek kódu k vložení kódu F# do objektu výrazu a následnému vytištění kódu jazyka F#, který představuje výraz. Funkce println je definována, která obsahuje rekurzivní funkci print , která zobrazuje objekt výrazu jazyka F# (typu Expr) v popisné podobě. V modulech FSharp.Quotations.Patterns a FSharp.Quotations.DerivedPatterns existuje několik aktivních vzorů, které lze použít k analýze objektů výrazů. Tento příklad neobsahuje všechny možné vzory, které by se mohly objevit ve výrazu F#. Jakýkoli nerozpoznaný vzor aktivuje shodu se vzorem se zástupnými znamény (_) a vykresluje se pomocí ToString metody, která v typu vás informuje o aktivním vzoru, který Expr se má přidat do výrazu shody.

Kód

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 @@>

Výstup

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

Příklad 2

Popis

Pomocí tří aktivních vzorů v modulu ExprShape můžete také procházet stromy výrazů s menším počtem aktivních vzorů. Tyto aktivní vzory můžou být užitečné, když chcete procházet strom, ale nepotřebujete všechny informace ve většině uzlů. Při použití těchto vzorů se libovolný výraz jazyka F# shoduje s jedním z následujících tří vzorů: ShapeVar pokud je výraz proměnnou, ShapeLambda pokud je výraz výrazem lambda nebo ShapeCombination pokud je výrazem něco jiného. Pokud procházíte strom výrazů pomocí aktivních vzorů jako v předchozím příkladu kódu, musíte použít mnoho dalších vzorů pro zpracování všech možných typů výrazů jazyka F# a váš kód bude složitější. Další informace naleznete v tématu ExprShape.ShapeVar|ShapeLambda|Aktivní vzor ShapeCombination

Následující příklad kódu lze použít jako základ pro složitější procházení. V tomto kódu se vytvoří strom výrazu pro výraz, který zahrnuje volání funkce , add. Aktivní vzor SpecificCall se používá k detekci jakéhokoli volání add ve stromu výrazů. Tento aktivní vzor přiřadí argumenty volání k hodnotě exprList . V tomto případě jsou pouze dva, takže se vytáhnou a funkce se u argumentů nazývá rekurzivně. Výsledky se vloží do uvozovek kódu, který představuje volání mul pomocí operátoru splice (%%). Funkce println z předchozího příkladu slouží k zobrazení výsledků.

Kód v ostatních aktivních větvích vzorů pouze znovu vygeneruje stejný strom výrazu, takže jedinou změnou výsledného výrazu je změna z add na mul.

Kód

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

Výstup

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

Viz také