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 Expr
yazı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 println
print
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ı add
iç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 add
mul
yerine 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))