Kod teklifleri

Bu makalede, F# kod ifadelerini program aracılığıyla oluşturma ve bu ifadelerle çalışmanız için bir dil özelliği olan kod tırnak işaretleri açıklanmıştır. Bu özellik, F# kodunu temsil eden soyut bir söz dizimi ağacı oluşturmana olanak sağlar. Daha sonra soyut söz dizimi ağacı, uygulamanın ihtiyaçlarına göre çapraz geçiş ve işlenebilir. Örneğin ağacı F# kodu oluşturmak veya başka bir dilde kod oluşturmak için kullanabilirsiniz.

Tırnak içine alınarak ifade

Tırnak içine alan ifade, kodunda program kapsamında derlenmiş değil, F# ifadesini temsil eden bir nesne olarak derlenmiş şekilde ayrılmış bir F# ifadesidir. Tırnak içine alınan bir ifadeyi iki şekilde işaretleyebilirsiniz: tür bilgileriyle veya tür bilgisi olmadan. Tür bilgilerini eklemek için ve sembollerini kullanarak <@ tırnak @> içine alınan ifadeyi sınırlandırma. Tür bilgilerine ihtiyacınız yoksa ve sembollerini <@@ @@> kullanırsanız. Aşağıdaki kod, türü ve yazlanmamış 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 bilgisi dahil değilken büyük bir ifade ağacından geçiş daha hızlıdır. Tür parametresinin, F# derleyicisi tür çıkarım algoritması tarafından belirlenen ifade türüne sahip olduğu, türü, türü olan, türüyle tırnak içine alınarak tırnak içine alınan bir ifadenin türü Expr<'T> olur. Tür bilgisi olmadan kod tırnaklarını kullanırken, tırnak içine alınan ifadenin türü, genel olmayan expr t t dır. Türlenmemiş nesneyi elde etmek için, türü türü alınan sınıfta Raw Expr özelliğini Expr çağırabilirsiniz.

Sınıfta tırnak içine alınarak ifadeleri kullanmadan program aracılığıyla F# ifade nesneleri Expr oluşturmasına olanak sağlayan çeşitli statik yöntemler vardır.

Kod alıntısı tam bir ifadeyi içermeli. Örneğin bağlama için hem bağlı adın tanımına hem de bağlamayı kullanan let başka bir ifadeye ihtiyacınız vardır. Ayrıntılı söz dizimlerinde bu, anahtar sözcüğünü izleyen bir in ifadedir. Bir modülün en üst düzeyinde, bu modülde yalnızca bir sonraki ifadedir, ancak 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ğerlendiricisini kullan gerekir. F# ifade nesnelerini değerlendirme ve yürütme desteği sağlar.

F# tırnakları 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 oluşturulan inline kısıtlama kod tırnak içinde korunur. İşlevin negate tırnak içine alan formu artık değerlendirebilirsiniz.

Expr türü

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

Splicing işleçleri

Birleştirme, sabit kod tırnaklarını program aracılığıyla veya başka bir kod teklifinden oluşturduğunuz ifadelerle birleştirmeye olanak sağlar. ve % %% işleçleri, bir F# ifade nesnesini kod teklifine eklemenize olanak sağlar. İşleci, türü girilen bir tırnak içine bir ifade nesnesi eklemek için kullanır, işleci kullanarak yazlanmamış bir tırnak içine bir türlanmamış % %% ifade nesnesi eklersiniz. Her iki işleç de birli ön ek işleçleridir. Bu nedenle expr türünde olmayan bir ifade ise aşağıdaki kod Expr geçerlidir.

<@@ 1 + %%expr @@>

ve expr türünde bir tırnak Expr<int> ise, aşağıdaki kod geçerlidir.

<@ 1 + %expr @>

Örnek 1

Description

Aşağıdaki örnek, F# kodunu bir ifade nesnesine koymak ve ardından ifadeyi temsil eden F# kodunu yazdırmak için kod tırnaklarının kullanımını gösterir. F# ifade nesnesini (türünde) kolay bir biçimde görüntüleyen bir println print recursive işlevi içeren Expr bir işlev tanımlanır. FSharp.Quotations.Patterns ve FSharp.Quotations.DerivedPatterns modüllerinde ifade nesnelerini analiz etmek için kullanılmaktadır. Bu örnek, bir F# ifadesinde görünse bile tüm olası desenleri içermemektedir. Tanınmayan desenler joker karakter deseniyle ( ) eşleşmeyi tetikler ve türüne göre eşleşme ifadenize eklenecek etkin deseni size sağlayan yöntemi kullanılarak _ ToString Expr 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

Description

Daha az etkin desene sahip ifade ağaçlarında çapraz geçiş yapmak için ExprShape modülünde üç etkin deseni de kullanabilirsiniz. Bu etkin desenler, bir ağaçtan geçiş yapmak istediğiniz ancak düğümlerin çoğunda tüm bilgilere ihtiyacınız yoksa yararlı olabilir. Bu desenleri kullanırsanız, herhangi bir F# ifadesi aşağıdaki üç desenden biri ile eşler: ifade bir değişkense, ifade bir lambda ifadesi ise veya ifade başka bir ShapeVar ShapeLambda ShapeCombination şeyse. Önceki kod örneğinde olduğu gibi etkin desenleri kullanarak bir ifade ağacında çapraz geçiş yaptıysanız, tüm olası F# ifade türlerini işlemek için çok daha fazla desen kullansanız, kodunuz daha karmaşık hale gelecektir. Daha fazla bilgi için, bkz. ExprShape.ShapeVar|ShapeLambda|ShapeCombination Active Pattern.

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ı içeren bir ifade için bir ifade ağacı add oluşturulur. SpecificCall etkin deseni, ifade ağacında herhangi bir çağrıyı add algılamak için kullanılır. Bu etkin desen, çağrının bağımsız değişkenlerini değere exprList atar. Bu durumda yalnızca iki tane vardır, bu nedenle bunlar çekilir ve işlev bağımsız değişkenler üzerinde tekrar tekrar çağrılır. Sonuçlar, splice işleci () kullanılarak bir çağrıyı mul temsil eden bir kod tırnak içine %% eklenir. Sonuçları println görüntülemek için önceki örnekteki işlevi kullanılır.

Diğer etkin desen dallarında yer alan kod yalnızca aynı ifade ağacını yeniden oluştursa da sonuçta elde edilen ifadede yapılan tek değişiklik ile olan add mul değişikliktir.

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.