Uvozovky kódu

Tento článek popisuje uvozovky kódu, funkci jazyka, která umožňuje generovat výrazy kódu F# a pracovat s nich prostřednictvím kódu programu. Tato funkce umožňuje vygenerovat abstraktní strom syntaxe, který představuje kód jazyka F#. Abstraktní strom syntaxe se pak může prochovat a zpracovávat podle potřeb vaší aplikace. Strom můžete použít například k vygenerování kódu F# nebo k vygenerování kódu v jiném jazyce.

Výrazy v uvozovce

Výraz v uvozovce je výraz jazyka 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 jazyka F#. Výraz v uvozovce 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žijte symboly a k <@ @> oddělení výrazu v uvozovek. Pokud informace o typu nepotřebujete, použijte symboly a <@@ @@> . Následující kód ukazuje typové a netypové 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 nezahrnujete informace o typu. Výsledný typ výrazu uvozovek se symboly typu je , kde parametr typu má typ výrazu určený algoritmem pro odvozování typů kompilátoru Expr<'T> jazyka F#. Při použití uvozovek kódu bez informací o typu je typem výrazu v uvozovkách neobecná typ Expr. Vlastnost Raw na typové třídě můžete volat, abyste Expr získali netypový Expr objekt.

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

Uvozovky kódu musí obsahovat úplný výraz. Například pro vazbu potřebujete definici vázaného názvu i jiný let výraz, který tuto vazbu používá. V podrobné syntaxi je to výraz, který následuje klíčové in slovo . Na nejvyšší úrovni modulu je to jen další výraz v modulu, ale v uvozovkách je to explicitně nutné.

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

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

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

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

K vyhodnocení uvozovek jazyka F# musíte použít vyhodnocovač pro uvozovky 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. Uvažujte 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é funkcí se uchovává inline v uvozovkách kódu. Formulář negate funkce v uvozovce se teď může vyhodnotit.

Typ expr

Instance typu Expr představuje výraz jazyka F#. Obecné i neobecná typy jsou Expr zdokumentovány v dokumentaci ke knihovně jazyka F#. Další informace najdete v tématu Obor názvů FSharp.Quotations a třída Quotations.Expr.

Operátory spojování

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

<@@ 1 + %%expr @@>

A pokud expr je typová uvozovka Expr<int> typu , 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 pro vkládaní kódu F# do objektu výrazu a potom tisk kódu F#, který představuje výraz. Je definována funkce, která obsahuje rekurzivní funkci, která zobrazí objekt výrazu println print 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 jazyka F#. Nerozpoznaný vzor aktivuje shodu se vzorem se zástupnými znaky ( ) a vykreslí se pomocí metody , která u typu umožňuje znát aktivní vzor, který chcete přidat do výrazu _ ToString Expr 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

Tři aktivní vzory v modulu ExprShape můžete použít také k procházování stromů výrazů s menším počtem aktivních vzorů. Tyto aktivní vzory mohou být užitečné, když chcete procházet strom, ale nepotřebujete všechny informace ve většině uzlů. Když použijete tyto vzory, jakýkoli výraz F# odpovídá jednomu z následujících tří vzorů: pokud je výraz proměnná, pokud je výraz výrazem lambda nebo pokud je výrazem něco ShapeVar ShapeLambda ShapeCombination jiného. Pokud prochádíte strom výrazů pomocí aktivních vzorů jako v předchozím příkladu kódu, budete muset 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 najdete v tématu ExprShape.ShapeVar|ShapeLambda|ShapeCombination Active Pattern.

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ýrazů pro výraz, který zahrnuje volání funkce add . Aktivní vzor SpecificCall slouží k detekci jakéhokoli volání ve add stromu výrazů. Tento aktivní vzor přiřadí argumenty volání k exprList hodnotě. V tomto případě jsou k dispozici pouze dvě, takže se načtou a funkce se volá rekurzivně pro argumenty. Výsledky se vloží do uvozovek kódu, který představuje volání pomocí mul operátoru splice ( %% ). Funkce println z předchozího příkladu slouží k zobrazení výsledků.

Kód v ostatních větvích aktivního vzoru pouze znovu vygeneruje stejný strom výrazů, 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é