Julia中,Lisp留下的最好的禮物就是metaprogramming。Julia體現了Lisp "code as a data structure"的精神
每個Julia程式都是生於字串
prog = "1 + 1" # "1 + 1"
接著,字串會被解析,成為叫作Expr
的物件
ex1 = parse(prog) # :(1 + 1)
typeof(ex1) # Expr
Expr
包含了3個部份:
head
:以一個Symbol
來表示這個Expr
的種類,而Symbol
你可以把他當成電腦讀的懂的字串args
:代表著expression的參數,你可以想成是這些元素組成這個expression的typ
:表示這個Expr
他回傳值的型別ex1.head # :call
ex1.args # [:+, 1, 1]
ex1.typ # Any
Expr
也可以被直接建構,運算子會以prefix的方式建構,像以下這個樣子
ex2 = Expr(:call, :+, 1, 1)
ex1 == ex2 # true
dump(ex2) # 這可以把Expr的資訊印出來
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Int64 1
typ: Any
要宣告Symbol
很簡單,只要加個:
就好
:foo # :foo
typeof(:foo) # Symbol
:foo == Symbol("foo") # true
要宣告Expr
也很簡單,只要用:()
將他包住就可以了
ex = :(a + b * c + 1) # :(a + b * c + 1)
大家可以看看他的結構,會發現ex.args
的第3個是個Expr
,Expr
還可以展開成其他東西
Julia的語法解析會這樣一層一層解析下去
dump(ex)
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol +
2: Symbol a
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol *
2: Symbol b
3: Symbol c
typ: Any
4: Int64 1
typ: Any
另一種宣告Expr
的方法
ex = quote
x = 1
y = 2
x + y
end
quote
x = 1
y = 2
x + y
end
Julia語言自己可以解析自己是個強大的功能,但是更強大的在後面
a = 1;
ex = :($a + b) # :(1 + b)
這邊可以讓作為變數的a
直接將值帶入,如此一來就可以直接做推斷
ex = :(a in $:((1, 2, 3)) ) # :(a in (1,2,3))
接著,解析完之後就是執行了!!
eval(:(1 + 2)) # 3
eval()
會直接執行Expr
,他會直接取用global variable,更可能會更改到他們
ex = :(x = 1)
x # LoadError: UndefVarError: x not defined
這段程式當中只有建構Expr
,x
沒有被宣告
eval(ex)
x # 1
執行之後,x
就有值了
應用以上的原理,macro
就是這麼一個東西,他像一個函式一樣可以接受參數,他會回傳編譯過的Expr
並執行
macro sayhello()
return :( println("Hello, world!") )
end
@sayhello
Hello, world!
@sayhello
這個macro將println("Hello, world!")
這段程式碼編譯後,呼叫@sayhello
就會直接執行了
macro sayhello(name)
return :( println("Hello, ", $name) )
end
@sayhello("Bob")
@sayhello "Bob"
Hello, Bob
Hello, Bob
參數有以上兩種給法,可以不用括弧
今天介紹了Julia的macro這個強大的工具,他其在在Lisp, C等等語言中也被應用的,Julia裏面有很多功能都被寫成macro的形式。