Megosztás a következőn keresztül:


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ú Exprkifejezé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))

Lásd még