Kod teklifleri

Bu makalede, F# kod ifadelerini program aracılığıyla oluşturmanıza ve bunlarla çalışmanıza olanak tanıyan bir dil özelliği olan kod teklifleri açıklanmaktadır. Bu özellik, F# kodunu temsil eden soyut bir söz dizimi ağacı oluşturmanıza olanak tanır. Daha sonra soyut söz dizimi ağacı, uygulamanızın gereksinimlerine göre geçirilebilir ve işlenebilir. Örneğin, F# kodu oluşturmak veya başka bir dilde kod oluşturmak için ağacı kullanabilirsiniz.

Tırnak içine alınmış ifadeler

Tırnak içine alınmış ifade, kodunuzdaki bir F# ifadesidir ve programınızın bir parçası olarak derlenmez, bunun yerine F# ifadesini temsil eden bir nesnede derlenmiş olur. Tırnak içine alınmış bir ifadeyi iki yoldan biriyle işaretleyebilirsiniz: tür bilgileriyle veya tür bilgisi olmadan. Tür bilgilerini eklemek istiyorsanız, simgeleri <@@> ve tırnak içine alınmış ifadeyi sınırlandırmak için kullanırsınız. Tür bilgilerine ihtiyacınız yoksa ve @@>simgelerini <@@ kullanırsınız. Aşağıdaki kod, yazılan ve yazılmamış tırnak işaretlerini gösterir.

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

Tür bilgilerini eklemezseniz büyük bir ifade ağacını dolaşma işlemi daha hızlıdır. Türlenmiş simgelerle alıntı yapılan bir ifadenin sonuç türü, Expr<'T>tür parametresinin F# derleyicisinin tür çıkarımı algoritması tarafından belirlenen ifadenin türüne sahip olduğu türüdür. Kod tırnaklarını tür bilgisi olmadan kullandığınızda, tırnak içine alınan ifadenin türü genel olmayan tür Expr'dır. Yazılmamış Expr nesneyi almak için türü belirtilen Expr sınıfta Raw özelliğini çağırabilirsiniz.

Alıntılanmış ifadeler kullanmadan sınıfta program aracılığıyla Expr F# ifade nesneleri oluşturmanıza olanak sağlayan çeşitli statik yöntemler vardır.

Kod teklifi eksiksiz bir ifade içermelidir. Örneğin bağlama let için hem ilişkili adın tanımına hem de bağlamayı kullanan başka bir ifadeye ihtiyacınız vardır. Ayrıntılı söz diziminde, bu anahtar sözcüğü izleyen in bir ifadedir. Bir modülün en üst düzeyinde, bu modülde yalnızca bir sonraki ifadedir, ancak bir tırnak içinde açıkça gereklidir.

Bu nedenle, aşağıdaki ifade geçerli değildir.

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

Ancak aşağıdaki ifadeler geçerlidir.

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

F# tırnaklarını değerlendirmek için F# Tırnak Değerlendiricisi'ni kullanmanız gerekir. F# ifade nesnelerini değerlendirmek ve yürütmek için destek sağlar.

F# tırnak işaretleri, tür kısıtlama bilgilerini de korur. Aşağıdaki örneği inceleyin:

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

İşlev tarafından inline oluşturulan kısıtlama kod teklifinde tutulur. İşlevin negate alıntılanan formu artık değerlendirilebilir.

İfade türü

Türün Expr bir örneği bir F# ifadesini temsil eder. Hem genel hem de genel Expr olmayan türler F# kitaplığı belgelerinde belgelenmiştir. Daha fazla bilgi için bkz . FSharp.Quotations Ad Alanı ve Quotations.Expr Sınıfı.

Splicing işleçleri

Splicing, değişmez kod tekliflerini program aracılığıyla veya başka bir kod teklifinden oluşturduğunuz ifadelerle birleştirmenizi sağlar. % ve %% işleçleri, bir kod teklifine F# ifade nesnesi eklemenize olanak tanır. Işlecini % , yazılan bir tırnak içine yazılan ifade nesnesi eklemek için kullanırsınız; işlecini %% kullanarak yazılmamış bir ifade nesnesini yazılmamış bir tırnak içine eklersiniz. her iki işleç de birli ön ek işleçleridir. Bu nedenle türündeki Expryazılmamış bir ifadeyseexpr, aşağıdaki kod geçerlidir.

<@@ 1 + %%expr @@>

Türü yazılan bir tırnak işareti Expr<int>iseexpr, aşağıdaki kod geçerlidir.

<@ 1 + %expr @>

Örnek 1

Açıklama

Aşağıdaki örnek, F# kodunu bir ifade nesnesine yerleştirmek ve ardından ifadeyi temsil eden F# kodunu yazdırmak için kod tırnaklarının kullanımını gösterir. bir F# ifade nesnesini (türüExpr) kolay biçimde görüntüleyen özyinelemeli bir işlev içeren bir işlev printlnprint tanımlanır. FSharp.Quotations.Patterns ve FSharp.Quotations.DerivedPatterns modüllerinde, ifade nesnelerini analiz etmek için kullanılabilecek birkaç etkin desen vardır. Bu örnek, bir F# ifadesinde görünebilecek tüm olası desenleri içermez. Tanınmayan tüm desenler joker karakter deseni (_) ile eşleşmeyi tetikler ve türündeki Expr eşleşme ifadenize eklenecek etkin deseni bilmenizi sağlayan yöntemi kullanılarak ToString işlenir.

Kod

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

Çıktı

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

Örnek 2

Açıklama

İfade ağaçlarını daha az etkin desenle çapraz geçiş yapmak için ExprShape modülündeki üç etkin deseni de kullanabilirsiniz. Bu etkin desenler, bir ağaçtan geçiş yapmak istediğinizde yararlı olabilir, ancak düğümlerin çoğunda tüm bilgilere ihtiyacınız yoktur. Bu desenleri kullandığınızda, herhangi bir F# ifadesi aşağıdaki üç desenden biriyle eşleşir: ShapeVar ifade bir değişkense, ShapeLambda ifade bir lambda ifadesiyse veya ShapeCombination ifade başka bir şeyse. Önceki kod örneğinde olduğu gibi etkin desenleri kullanarak bir ifade ağacından geçiş yaparsanız, tüm olası F# ifade türlerini işlemek için çok daha fazla desen kullanmanız gerekir ve kodunuz daha karmaşık olur. Daha fazla bilgi için bkz . ExprShape.ShapeVar|ShapeLambda|ShapeCombination Etkin Deseni.

Aşağıdaki kod örneği, daha karmaşık geçişler için temel olarak kullanılabilir. Bu kodda, işlev çağrısı addiçeren bir ifade için bir ifade ağacı oluşturulur. SpecificCall etkin deseni, ifade ağacındaki herhangi bir çağrıyı algılamak için add kullanılır. Bu etkin desen, çağrısının bağımsız değişkenlerini değere exprList atar. Bu durumda yalnızca iki tane vardır, bu nedenle bunlar çıkarılır ve işlev bağımsız değişkenlerde özyinelemeli olarak çağrılır. Sonuçlar, splice işleci (%% kullanılarak çağrıyı mul temsil eden bir kod teklifine eklenir. Önceki println örnekteki işlev sonuçları görüntülemek için kullanılır.

Diğer etkin desen dallarındaki kod aynı ifade ağacını yeniler, dolayısıyla sonuçta elde edilen ifadedeki tek değişiklik olan addmulyerine olarak değişir.

Kod

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

Çıktı

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

Ayrıca bkz.