Kód idézőjelek
Ez a cikk a kód idézőjeleit ismerteti, amely egy olyan nyelvi funkció, amellyel programozott módon hozhat létre és használhat F#-kódkifejezéseket. Ez a funkció lehetővé teszi az F#-kódnak megfelelő absztrakt szintaxisfa létrehozását. Az absztrakt szintaxisfa ezután az alkalmazás igényeinek megfelelően bejárható és feldolgozható. A fa használatával például F#-kódot hozhat létre, vagy más nyelven hozhat létre kódot.
Idézett kifejezések
Az idézett kifejezés olyan F# kifejezés a kódban, amely úgy van elválasztva, hogy az ne a program részeként legyen lefordítva, hanem egy F#-kifejezést képviselő objektumba van lefordítva. Az idézett kifejezéseket kétféleképpen jelölheti meg: típusadatokkal vagy típusinformációk nélkül. Ha típusadatokat szeretne megadni, használja a szimbólumokat <@
, és @>
tagolja az idézett kifejezést. Ha nincs szüksége típusadatokra, használja a szimbólumokat <@@
és @@>
a . Az alábbi kód beírt és nem beírt idézeteket jelenít meg.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
Ha nem tartalmaz típusadatokat, a nagy kifejezésfán való áthaladás gyorsabb. A beírt szimbólumokkal Expr<'T>
idézett kifejezés eredményének típusa az, ahol a típusparaméter az F# fordító típuskövető algoritmusa által meghatározott kifejezés típusát határozza meg. Ha típusinformációk nélküli kód idézőjeleket használ, az idézett kifejezés típusa az Expr nem általános típus. A beírt Expr
osztály Raw tulajdonságát meghívhatja a nem beírt Expr
objektum lekéréséhez.
A különböző statikus módszerek lehetővé teszik az F#-kifejezésobjektumok programozott módon történő generálásához az Expr
osztályban idézett kifejezések használata nélkül.
A kód idézőjelének teljes kifejezést kell tartalmaznia. let
Egy kötéshez például a kötött név definíciójára és egy másik, a kötést használó kifejezésre is szükség van. A részletes szintaxisban ez a kifejezés a kulcsszót in
követi. A modul legfelső szintjén ez csak a modul következő kifejezése, de egy idézetben kifejezetten kötelező.
Ezért a következő kifejezés érvénytelen.
// Not valid:
// <@ let f x = x + 1 @>
A következő kifejezések azonban érvényesek.
// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
let f x = x + 10
f 20
@>
Az F# idézőjelek kiértékeléséhez az F# árajánlat-kiértékelőt kell használnia. Támogatja az F#-kifejezésobjektumok kiértékelését és végrehajtását.
Az F# idézőjelek a típusmegkötés adatait is megőrzik. Vegyük a következő példát:
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
A függvény által inline
létrehozott kényszer megmarad a kód idézőjelében. A negate
függvény idézett űrlapja mostantól kiértékelhető.
Kiutasítás típusa
A típus egy példánya Expr
egy F# kifejezést jelöl. Az általános és a nem általános Expr
típusok is dokumentálva vannak az F#-kódtár dokumentációjában. További információ: FSharp.Quotations Namespace and Quotations.Expr Class.
Splicing operátorok
A szorzás lehetővé teszi, hogy a literális kód idézőjeleit olyan kifejezésekkel kombinálja, amelyeket programozott módon vagy egy másik kódajánlatból hozott létre. Az %
operátorok %%
segítségével F# kifejezésobjektumot adhat hozzá egy kódajánlathoz. Az operátorral %
beszúrhat egy beírt kifejezésobjektumot egy beírt idézőjelbe, az %%
operátorral pedig beszúrhat egy nem beírt kifejezésobjektumot egy be nem írott idézőjelbe. Mindkét operátor előtag nélküli operátor. Így ha expr
nem beírt típusú Expr
kifejezés, akkor a következő kód érvényes.
<@@ 1 + %%expr @@>
expr
Ha pedig egy beírt idézőjel típusúExpr<int>
, akkor az alábbi kód érvényes.
<@ 1 + %expr @>
1. példa
Leírás
Az alábbi példa azt szemlélteti, hogy a kód idézőjelek használatával F#-kódot helyezhet egy kifejezésobjektumba, majd kinyomtathatja a kifejezést jelképező F# kódot. Egy függvény println
definiálva van, amely egy rekurzív függvényt print
tartalmaz, amely felhasználóbarát formátumban jelenít meg egy F# kifejezésobjektumot (típus Expr
). Az FSharp.Quotations.Patterns és az FSharp.Quotations.DerivedPatterns modulban számos aktív minta található, amelyek a kifejezésobjektumok elemzéséhez használhatók. Ez a példa nem tartalmazza az összes lehetséges mintát, amely egy F# kifejezésben jelenhet meg. A fel nem ismert minták egyezést váltanak ki a helyettesítő karaktermintával (_
) és a ToString
metódussal jelennek meg, amely a Expr
típuson tudatja a találati kifejezéshez hozzáadni kívánt aktív mintázattal.
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 @@>
Hozam
fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10
2. példa
Leírás
Az ExprShape modul három aktív mintáját is használhatja a kevesebb aktív mintával rendelkező kifejezésfákkal való áthaladáshoz. Ezek az aktív minták akkor lehetnek hasznosak, ha egy fán szeretne áthaladni, de a csomópontok többségében nincs szüksége az összes információra. Ha ezeket a mintákat használja, minden F#-kifejezés megfelel az alábbi három minta egyikének: ShapeVar
ha a kifejezés változó, ShapeLambda
ha a kifejezés lambda kifejezés, vagy ShapeCombination
ha a kifejezés bármi más. Ha az aktív mintákkal lépked egy kifejezésfán, mint az előző kód példában, sokkal több mintával kell kezelnie az összes lehetséges F#-kifejezéstípust, és a kód összetettebb lesz. További információ: ExprShape.ShapeVar|ShapeLambda|ShapeCombination Active Pattern.
Az alábbi példakód az összetettebb bejárások alapjaként használható. Ebben a kódban egy kifejezésfa jön létre egy függvényhívást tartalmazó kifejezéshez. add
A SpecificCall aktív mintázata a kifejezésfán lévő összes hívás észlelésére add
szolgál. Ez az aktív minta a hívás argumentumait rendeli hozzá az exprList
értékhez. Ebben az esetben csak kettő van, ezért ezek ki lesznek húzva, és a függvényt rekurzívan hívják az argumentumokon. Az eredmények olyan kód idézőjelbe kerülnek, amely a splice operátor (%%
) használatával hívja meg a hívástmul
. Az println
előző példában szereplő függvény az eredmények megjelenítésére szolgál.
A többi aktív mintaágban lévő kód újragenerálja ugyanazt a kifejezésfát, így az eredményül kapott kifejezésben az egyetlen változás a váltás add
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
Hozam
1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))