iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 27
0

其實之前在寫R的時候就用到了後來所知道的metaprogramming

以前為了想要讓自己用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底下如何使用。

Metaprogramming in Julia

在Julia裡頭,基本上就是得先理解所謂的expression,其本質就是一個object。而這個object主要是由head(裡頭放的是標示著這個object是哪種expression的標記Symbol)及args(裡頭放的是expression的參數,甚至可以是另一個expression)。 不過大概看了一下官方文件的說明,發覺其實在Julia中,metaprogramming的概念與在R語言當中真的很類似,比如說:

  • Code is data
  • Code is a tree
  • Code can generate code

這些特點都是兩個語言在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

上一篇
[Day 26] 關於Julia的Performance
下一篇
[Day 28] 初識Julia裡頭的Module
系列文
When Bioinfo met Julia: Bioinformatician的30天Julia學習之路32

1 則留言

0
杜岳華
iT邦新手 5 級 ‧ 2018-10-28 23:09:58

要用:

julia> macro sayhello(name)
           return :( println("Hello, ", $name) )
       end

這邊要注意的是,在使用變數的時候,跟函式裡不一樣,要加上 $ 來區別變數跟 Symbol。
Expression,拆解到最小的單位,不是值就是 Symbol,Symbol 可以直接對映到 token 的概念,因為他還沒有賦值,所以編譯器暫時不知道他是什麼東西,就是個 Symbol。
但在這個例子中不一樣,你想做的是值的內插,就是將你的變數代換成值放到你指定的程式碼當中。
所以你應該要把他當成變數處理。

這邊需要想想,我自己有時候也會卡住XD

我要留言

立即登入留言