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