根據百度百科的定義,元程式設計(Meta-Programming)是指某類程式編寫或者操縱其他程式(或者自身)作為它們的資料,或者在運作時完成部分本應在編譯時完成的工作。編寫元程式的語言稱之為元語言,被操作的語言稱之為目智語言。一門語言同時也是自身的元語言的能力稱之為反射。換句話說,具有元程式設計能力的語言,可以自己來編寫自己,即Julia語言代碼中可以包含Julia語言代碼,這樣可以在運作期間動态的執行一些業務邏輯,是以其功能非常強大。
下面将重點介紹一下Julia語言的元程式設計用法:
julia> e1 = Meta.parse("2 * 3")
:(2 * 3)
julia> typeof(e1)
Expr
julia> e1.head
:call
julia> e1.args
3-element Array{Any,1}:
:*
2
3
julia> dump(e1)
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol *
2: Int64 2
3: Int64 3
julia> ex2 = Expr(:call,:*,2,3)
:(2 * 3)
julia> e3 = :(2*3)
:(2 * 3)
julia> e1 == ex2
true
julia> e1 == e3
true
julia> Meta.show_sexpr(e1)
(:call, :*, 2, 3)
julia> var_x = :x
:x
julia> typeof(var_x)
Symbol
julia> var_y = Symbol("y")
:y
julia> var_z = Symbol("var",'_',"z")
:var_z
julia> typeof(e1)
Expr
julia> exq = quote
x = 1
y = 3
sin(x)+y
end
quote
#= REPL[34]:2 =#
x = 1
#= REPL[34]:3 =#
y = 3
#= REPL[34]:4 =#
sin(x) + y
end
julia> typeof(exq)
Expr
julia> eval(exq)
3.8414709848078967
julia> eval(:(2 * 3))
6
julia> x = 2
2
julia> ex = :($x+sin($x))
:(2 + sin(2))
julia> eval(ex)
2.909297426825682
julia> ex = :(x in $:((1,2,3)))
:(x in (1, 2, 3))
julia> eval(ex)
true
julia> args = [:x, :y, :m];
julia> ex = :(f(1, $(args...)))
:(f(1, x, y, m))
julia> Meta.quot(Expr(:$, :(1+2)))
:($(Expr(:quote, :($(Expr(:$, :(1 + 2)))))))
julia> mq = Meta.quot(Expr(:$, :(1+2)))
:($(Expr(:quote, :($(Expr(:$, :(1 + 2)))))))
julia> eval(mq)
3
julia> e = dump(Meta.parse(":x"))
QuoteNode
value: Symbol x
從上述代碼示例中可知,Julia提供了一個Meta類,其中可以用 Meta.parse(str)來對一個字元串進行解析,并傳回 :(2 * 3) , 這個傳回的值有一個冒号,由于後面是一個表達式,用typeof(e1)傳回Expr。而表達式e1有head屬性和args屬性,可以傳回操作和參數資訊。同樣的, ex2 = Expr(:call,:*,2,3) 也可以用來定義一個表達式,而最簡單的定義方式為 e3 = :(2*3) 。這三種方式定義的表達式是相同的,通過邏輯比較則是相同的。
另外,var_x = :x 也可以定義符号對象,即 Symbol , 即typeof(var_x) 傳回 Symbol 。而Symbol("var",'_',"z") 定義了一個:var_z的Symbol 。對于多行的表達式定義,可以通過quote ... end進行定義。而表達式可以通過 eval(exq)函數求值。
最後,表達式中還可以用符号$進行變量插值操作,即将變量替換成值。 如 x = 2 , ex = :($x+sin($x)) 傳回 :(2 + sin(2)) 。且定義中還支援語句,如 ex = :(x in $:((1,2,3))) ,由于x = 2 ,是以x是屬于 (1,2,3) 内的,eval傳回true 。
Julia還支援宏,宏用macro聲明,調用時用@函數名進行調用:
julia> macro sayhello(name)
return :( println("Hello, ", $name) )
end
@sayhello (macro with 1 methods)
julia> @sayhello("julia")
Hello, julia
元程式設計中也可以引用自定義的函數等,這将大大提高了靈活性:
julia> function myf(x,y)
x + y + 1
end
myf (generic function with 1 method)
julia> ex = :(myf(1,2))
:(myf(1, 2))
julia> eval(ex)
4
julia> ex = Meta.parse("myf(1,2)+3")
:(myf(1, 2) + 3)
julia> eval(ex)
7
julia> A = [ 1 2 3 ; 4 5 6]
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> A'
3×2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
1 4
2 5
3 6
julia> ex = Meta.parse("A= [ 1 2 3 ; 4 5 6]")
:(A = [1 2 3; 4 5 6])
julia> eval(ex)
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> ex = Meta.parse("A2= [ 1 2 3 ; 4 5 6]")
:(A2 = [1 2 3; 4 5 6])
julia> A2
ERROR: UndefVarError: A2 not defined
julia> eval(ex)
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> A2
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> ex = Meta.parse("A3 = A2'")
:(A3 = A2')
julia> eval(ex)
3×2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
1 4
2 5
3 6
julia> A3
3×2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
1 4
2 5
3 6
julia> ex = Meta.parse("a=1;b=2;z=sin(a)*b")
:($(Expr(:toplevel, :(a = 1), :(b = 2), :(z = sin(a) * b))))
julia> eval(ex)
1.682941969615793
從上述示例中可知,我們可以定義自己的函數,并可以作為字元串中進行表達式邏輯編寫,通過Meta.parse方法進行解析,同時,文本中可以進行變量的定義,當通過Eval執行後,在外部可以進行變量的通路。如 ex = Meta.parse("A= [ 1 2 3 ; 4 5 6]") 在未執行 eval(ex)時,外部通路A則抛出錯誤:ERROR: UndefVarError: A2 not defined 。而執行eval(ex)後,外部通路A則傳回 [ 1 2 3 ; 4 5 6 ] 。
Meta.parse方法也支援多行語句,中間用;分割,如ex = Meta.parse("a=1;b=2;z=sin(a)*b") ,它傳回:($(Expr(:toplevel, :(a = 1), :(b = 2), :(z = sin(a) * b)))) ,當執行eval時,傳回1.682941969615793 。