以前為了想要讓自己用R寫的一段程式碼可以自動化地生成一堆變數,然後這些變數還可以在後面繼續使用時,就在找有什麼方式可以達成這件事,還記得那個時候在StackOverflow
上面看到的解法就是使用eval()
+ parse()
的方式,像這樣:
> ls()
character(0)
> for (i in 1:5) eval(parse(text=paste0("x", i, "<-", i)))
> ls()
[1] "i" "x1" "x2" "x3" "x4" "x5"
我想這種需求到了Julia底下依舊不會少,更何況就算我自己不寫,也常常會用到一堆別人寫的macro
,因此絕對有必要理解一下這個東西在Julia底下如何使用。
在Julia裡頭,基本上就是得先理解所謂的expression
,其本質就是一個object
。而這個object
主要是由head
(裡頭放的是標示著這個object
是哪種expression
的標記Symbol
)及args
(裡頭放的是expression
的參數,甚至可以是另一個expression
)。 不過大概看了一下官方文件的說明,發覺其實在Julia中,metaprogramming的概念與在R語言當中真的很類似,比如說:
這些特點都是兩個語言在metaprogramming上面很像的地方,但R語言裡頭在定義Macro
這點上就不是原生的R幹的到的事情,它必須得先安裝gtools
這個套件才辦得到,而在Julia裡寫Macro就相對簡單了。讓我簡單的範例來展示這小小的差異:
julia> macro setDict(A, B)
return :( Dict($A => $B) )
end
@setDict (macro with 1 method)
julia> x = @setDict "a" "b"
Dict{String,String} with 1 entry:
"a" => "b"
julia> x["a"]
"b"
那麼在R底下,我們要如何寫同樣的macro呢?
> library(gtools)
> setDictR <- defmacro(key, val, eval(parse(text = paste0("list(", key, "=\"", val, "\")")))})
> x <- setDictR("a", "b")
> x
$a
[1] "b"
從這個例子可以看到在R裡頭要寫一樣的東西,真的是麻煩了許多。
But 我自己在嘗試官方文件當中的範例@sayhello
時,卻不斷地發生UndefVarError
錯誤,不知道阿杜有沒有什麼解決辦法?
julia> macro sayhello()
return :( println("Hello, world!") )
end
@sayhello (macro with 1 method)
julia> @sayhello()
Hello, world!
julia> macro sayhello(name)
return :( println("Hello, ", name) )
end
@sayhello (macro with 2 methods)
julia> @sayhello("human")
ERROR: UndefVarError: name not defined
Stacktrace:
[1] top-level scope at none:0
要用:
julia> macro sayhello(name)
return :( println("Hello, ", $name) )
end
這邊要注意的是,在使用變數的時候,跟函式裡不一樣,要加上 $ 來區別變數跟 Symbol。
Expression,拆解到最小的單位,不是值就是 Symbol,Symbol 可以直接對映到 token 的概念,因為他還沒有賦值,所以編譯器暫時不知道他是什麼東西,就是個 Symbol。
但在這個例子中不一樣,你想做的是值的內插,就是將你的變數代換成值放到你指定的程式碼當中。
所以你應該要把他當成變數處理。
這邊需要想想,我自己有時候也會卡住XD