Cytaty kodu (F#)
W tym temacie opisano Kod notowań, funkcja języka, który pozwala generować i pracować z wyrażeniami kod F# programowo.Ta funkcja pozwala wygenerować drzewo składni abstrakcyjnej, która reprezentuje kod F#.Można następnie pokonywany drzewo składni abstrakcyjnej i przetwarzane zgodnie z potrzebami aplikacji.Na przykład można użyć drzewa, aby wygenerować kod F# lub wygenerować kod w inny język.
Wyrażenia w cudzysłowach
A cytowane wyrażenia jest wyrażeniem F# w kodzie, który jest rozdzielany w taki sposób, że nie jest skompilowany jako część programu, ale zamiast tego jest skompilowany do obiektu, który reprezentuje wyrażenie F#.Można oznaczyć wyrażenie w cudzysłowie w jeden z dwóch sposobów: z informacji o typie lub bez informacji o typie.Jeśli chcesz dołączyć informacje o typie, aby używać symboli<@ i @> do rozdzielenia wyrażenie w cudzysłowie.Jeśli typ informacji nie jest konieczne, aby używać symboli <@@ i @@>.Poniższy kod ilustruje notowania maszynowy i bez typu.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
Przeglądanie drzewa dużych wyrażenie jest szybsza, jeśli nie zawiera informacji o typie.Wynikowy typ wyrażenia notowane symbolami wpisywany jest Expr<'T>, gdzie parametr typu ma typ wyrażenia określone przez kompilator F# typu wnioskowanie algorytmu.Użycie kodu notowania bez informacji o typie, typ wyrażenie w cudzysłowie jest typ rodzajowy nie wyrażenie.Można wywołać Raw właściwość maszynowy Expr klasy w celu uzyskania bez typu Expr obiektu.
Istnieją różne metody statyczne, które pozwala generować F# wyrażenie obiektów programowo w Expr klasa bez użycia cytowane wyrażenia.
Należy zauważyć, że oferty kod musi zawierać pełne wyrażenie.Dla let wiążące, na przykład, należy definicji Nazwa związany i dodatkowe wyrażenie używa wiązania.W pełnej składni jest następujące wyrażenie in słowa kluczowego.Na najwyższym poziomie w module to po prostu następnego wyrażenia w module, ale w ofercie, jest wyraźnie wymagane.
W związku z tym następujące wyrażenie nie jest prawidłowy.
// Not valid:
// <@ let f x = x + 1 @>
Jednak poniższe wyrażenia są prawidłowe.
// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
let f x = x + 10
f 20
@>
Aby użyć kodu notowań, należy dodać deklaracji przywozowej (za pomocą open słowa kluczowego), który otwiera Microsoft.FSharp.Quotations obszaru nazw.
F# PowerPack zapewnia obsługę dla oceny oraz wykonywania obiektów wyrażenie F#.
Wyrażenie typu
Wystąpienie Expr typu reprezentuje wyrażenie F#.Rodzajowa i nie uniwersalne Expr typy są opisane w dokumentacji biblioteki F#.Aby uzyskać więcej informacji, zobacz Microsoft.FSharp.Quotations — Przestrzeń nazw (F#) i Quotations.Expr — Klasa (F#).
Splatanie operatorów
Splatanie umożliwia łączenie notowania literału kodu z wyrażeniami utworzone programowo lub z innego kodu oferty.% i %% operatorów umożliwiają dodanie obiektu wyrażenie F# do oferty kodu.Za pomocą % operatora, aby wstawić obiekt wpisane wyrażenie do wpisywanych oferty; za pomocą %% operatora, aby wstawić obiekt bez typu wyrażenia bez cudzysłowu.Oba operatory są jednoargumentowe operatory przedrostkowe.Dlatego jeśli expr jest wyrażenie typu bez typu Expr, poniższy kod jest nieprawidłowy.
<@@ 1 + %%expr @@>
I jeśli expr jest wpisywany oferty typu Expr<int>, poniższy kod jest nieprawidłowy.
<@ 1 + %expr @>
Przykład
Opis
Poniższy przykład ilustruje notowań kodu do wprowadzane F# kod do obiektu wyrażenie, a następnie wydrukować kod, który reprezentuje wyrażenie: F#.Funkcja println jest zdefiniowany zawierający funkcji rekurencyjnej print , wyświetla obiekt wyrażenie F# (typu Expr) w przyjazny format.Istnieje kilka wzorców active Microsoft.FSharp.Quotations.Patterns i Microsoft.FSharp.Quotations.DerivedPatterns moduły, których można używać do analizowania obiektów w wyrażeniu.W tym przykładzie nie obejmuje wszystkich możliwych desenie, które mogą być wyświetlane w wyrażeniu F#.Dowolne Nierozpoznany wzorzec wyzwala dopasowanie do wzorca symboli wieloznacznych (_) i jest renderowane za pomocą ToString metody, które na Expr wpisz pozwala wiedzieć active deseń, aby dodać do wyrażenia dopasowania.
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 @@>
Dane wyjściowe
fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10
Przykład
Opis
Umożliwia także trzy active wzorców w moduł ExprShape przechodzenie drzew wyrażenie z mniej aktywnego wzorców.Te wzorce aktywny może być przydatne, gdy chcesz przechodzić przez drzewa, ale nie wymagają, aby wszystkie informacje w większości węzłów.Korzystając z tych deseni, każde wyrażenie F# pasuje do jednej z następujących trzech wzorców: ShapeVar , jeśli wyrażenie jest zmienna, ShapeLambda Jeśli wyrażenie jest wyrażenie lambda, lub ShapeCombination Jeśli wyrażenie jest inny.Jeśli drzewo wyrażenie jest Przechodzenie za pomocą active wzorców, jak w poprzednim przykładzie kodu, trzeba użyć wielu wzorców więcej do obsługi wszystkich możliwych typów wyrażeń F# i kod będzie bardziej złożony.Aby uzyskać więcej informacji, zobacz ExprShape.ShapeVar|ShapeLambda|ShapeCombination — Aktywny wzorzec (F#).
Poniższy przykład kodu może służyć jako podstawa dla bardziej złożonych traversals.W tym kodzie wyrażenia drzewa jest tworzony dla wyrażenie, które wymaga wywołania funkcji add.SpecificCall active deseń jest używane do wykrywania każde wywołanie do add w drzewie wyrażenie.Argumenty wywołania przypisuje do tego wzorca active exprList wartości.W tym przypadku istnieją tylko dwie tak te są pobierane i funkcja jest wywoływana rekursywnie na argumenty.Wyniki są wstawiane do oferty kod, który reprezentuje wywołanie do mul za pomocą operatora reperacyjnego (%%).println Funkcji z poprzedniego przykładu jest używany do wyświetlania wyników.
Kod w innych gałęziach active deseń regeneruje tylko tym samym drzewie wyrażenie tak tylko zmiana wyrażenie będące wynikiem jest zmiana z add do mul.
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
Dane wyjściowe
1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))