Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini menjelaskan kutipan kode, fitur bahasa yang memungkinkan Anda membuat dan bekerja dengan ekspresi kode F# secara terprogram. Fitur ini memungkinkan Anda menghasilkan pohon sintaks abstrak yang mewakili kode F#. Pohon sintaks abstrak kemudian dapat dilalui dan diproses sesuai dengan kebutuhan aplikasi Anda. Misalnya, Anda dapat menggunakan pohon untuk menghasilkan kode F# atau menghasilkan kode dalam beberapa bahasa lain.
Ekspresi yang dikutip
Ekspresi yang dikutip adalah ekspresi F# dalam kode Anda yang dibatasi sedih sehingga tidak dikompilasi sebagai bagian dari program Anda, tetapi sebaliknya dikompilasi ke dalam objek yang mewakili ekspresi F#. Anda dapat menandai ekspresi yang dikutip dengan salah satu dari dua cara: baik dengan informasi jenis atau tanpa informasi jenis. Jika Anda ingin menyertakan informasi jenis, Anda menggunakan simbol <@ dan @> untuk memisahkan ekspresi yang dikutip. Jika Anda tidak memerlukan informasi jenis, Anda menggunakan simbol <@@ dan @@>. Kode berikut menunjukkan kutipan yang ditik dan tidak dititip.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
Melintas pohon ekspresi besar lebih cepat jika Anda tidak menyertakan informasi jenis. Jenis ekspresi yang dihasilkan yang dikutip dengan simbol yang diketik adalah Expr<'T>, di mana parameter jenis memiliki jenis ekspresi seperti yang ditentukan oleh algoritma inferensi jenis kompilator F#. Saat Anda menggunakan kutipan kode tanpa informasi jenis, jenis ekspresi yang dikutip adalah Expr jenis non-generik. Anda dapat memanggil properti Raw pada kelas yang ditik Expr untuk mendapatkan objek yang tidak dititip Expr .
Ada berbagai metode statis yang memungkinkan Anda menghasilkan objek ekspresi F# secara terprogram di Expr kelas tanpa menggunakan ekspresi yang dikutip.
Kutipan kode harus menyertakan ekspresi lengkap.
let Untuk pengikatan, misalnya, Anda memerlukan definisi nama terikat dan ekspresi lain yang menggunakan pengikatan. Dalam sintaks verbose, ini adalah ekspresi yang mengikuti in kata kunci. Di tingkat atas dalam modul, ini hanyalah ekspresi berikutnya dalam modul, tetapi dalam kutipan, secara eksplisit diperlukan.
Oleh karena itu, ekspresi berikut tidak valid.
// Not valid:
// <@ let f x = x + 1 @>
Tetapi ekspresi berikut valid.
// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
let f x = x + 10
f 20
@>
Untuk mengevaluasi kutipan F#, Anda harus menggunakan Evaluator Kutipan F#. Ini menyediakan dukungan untuk mengevaluasi dan mengeksekusi objek ekspresi F#.
Kutipan F# juga menyimpan informasi batasan jenis. Pertimbangkan contoh berikut:
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
Batasan yang dihasilkan oleh inline fungsi dipertahankan dalam kutipan kode. Formulir negate kutipan fungsi sekarang dapat dievaluasi.
Jenis kedaluwarsa
Instans jenis Expr mewakili ekspresi F#. Jenis generik dan non-generik Expr didokumentasikan dalam dokumentasi pustaka F#. Untuk informasi selengkapnya, lihat FSharp.Quotations Namespace dan Quotations.Expr Class.
Operator splicing
Splicing memungkinkan Anda menggabungkan kutipan kode literal dengan ekspresi yang telah Anda buat secara terprogram atau dari kutipan kode lain. Operator % dan %% memungkinkan Anda menambahkan objek ekspresi F# ke dalam kutipan kode. Anda menggunakan % operator untuk menyisipkan objek ekspresi yang dititikkan ke dalam kutipan yang ditik; Anda menggunakan %% operator untuk menyisipkan objek ekspresi yang tidak dititip ke dalam kutipan yang tidak dititip. Kedua operator adalah operator awalan unary. Jadi jika expr adalah ekspresi jenis Expryang tidak dijenis , kode berikut valid.
<@@ 1 + %%expr @@>
Dan jika expr adalah kutipan jenis Expr<int>yang ditik , kode berikut valid.
<@ 1 + %expr @>
Contoh 1
Deskripsi
Contoh berikut mengilustrasikan penggunaan kutipan kode untuk memasukkan kode F# ke dalam objek ekspresi lalu mencetak kode F# yang mewakili ekspresi. Fungsi println didefinisikan yang berisi fungsi print rekursif yang menampilkan objek ekspresi F# (jenis Expr) dalam format yang ramah. Ada beberapa pola aktif dalam modul FSharp.Quotations.Patterns dan FSharp.Quotations.DerivedPatterns yang dapat digunakan untuk menganalisis objek ekspresi. Contoh ini tidak menyertakan semua pola yang mungkin muncul dalam ekspresi F#. Pola yang tidak dikenal memicu kecocokan dengan pola wildcard (_) dan dirender dengan menggunakan ToString metode , yang, pada jenisnya Expr , memungkinkan Anda mengetahui pola aktif untuk ditambahkan ke ekspresi kecocokan Anda.
Kode
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 @@>
Keluaran
fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10
Contoh 2
Deskripsi
Anda juga dapat menggunakan tiga pola aktif dalam modul ExprShape untuk melintasi pohon ekspresi dengan pola aktif yang lebih sedikit. Pola aktif ini dapat berguna ketika Anda ingin melintasi pohon tetapi Anda tidak memerlukan semua informasi di sebagian besar simpul. Saat Anda menggunakan pola-pola ini, ekspresi F# apa pun cocok dengan salah satu dari tiga pola berikut: ShapeVar jika ekspresi adalah variabel, ShapeLambda jika ekspresi adalah ekspresi lambda, atau ShapeCombination jika ekspresinya adalah hal lain. Jika Anda melintasi pohon ekspresi dengan menggunakan pola aktif seperti dalam contoh kode sebelumnya, Anda harus menggunakan lebih banyak pola untuk menangani semua jenis ekspresi F# yang mungkin, dan kode Anda akan lebih kompleks. Untuk informasi selengkapnya, lihat ExprShape.ShapeVar|ShapeLambda|Pola Aktif ShapeCombination.
Contoh kode berikut dapat digunakan sebagai dasar untuk traversal yang lebih kompleks. Dalam kode ini, pohon ekspresi dibuat untuk ekspresi yang melibatkan panggilan fungsi, add. Pola aktif SpecificCall digunakan untuk mendeteksi panggilan add apa pun di pohon ekspresi. Pola aktif ini menetapkan argumen panggilan ke exprList nilai . Dalam hal ini, hanya ada dua, sehingga ini ditarik keluar dan fungsi dipanggil secara rekursif pada argumen. Hasilnya disisipkan ke dalam kutipan kode yang mewakili panggilan dengan mul menggunakan operator splice (%%). Fungsi println dari contoh sebelumnya digunakan untuk menampilkan hasilnya.
Kode di cabang pola aktif lainnya hanya meregenerasi pohon ekspresi yang sama, sehingga satu-satunya perubahan dalam ekspresi yang dihasilkan adalah perubahan dari add ke mul.
Kode
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
Keluaran
1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))