2015-03-03 67 views
1

想象一下,我的意圖是修改傳遞給宏的s表達式的語法樹,方法是修改語法樹,然後評估修改後的參數。我可以做類似如下:在Clojure宏中評估修改參數的習慣用法是什麼?

(defmacro runargs [args] 
    (eval (cons (first args) " buckeroo"))) 

(runargs (println "hi there")) 

現在,這並不覺得習慣,因爲我已經疲憊不堪的我的代碼中間的大eval

現在我稍微修改,並拿出了以下內容:

(defmacro runargs [args] 
    `([email protected](cons (first args) " buckeroo"))) 

(runargs (println "hi there")) 

這已經解決了這個問題eval。但我仍然覺得這不太習語。

我的問題是:在Clojure宏中評估修改參數的習慣用法是什麼?

+0

你能提供您所需的輸出,無論是AST-明智的:如果宏是真正的s表達式的語法變換,那麼你可以在像你的數據將任何其他Clojure的數據結構表達操作和打印的? (因爲給定的代碼不會打印'buckeroo',而是'b u c k e r o o',我不確定這是否是有意的。) – xsc 2015-03-03 11:39:16

+0

我的歉意。我們假設這是有意的。 – hawkeye 2015-03-03 12:20:26

+0

使用宏的'技巧'是實際上有一個清晰的圖片,說明一旦宏展開後代碼的外觀。從這裏,你可以確定你想要做什麼的模板。一旦你有了這個,那麼這個宏是非常直接的定義。在大多數情況下,你不希望宏對參數進行評估 - 它更多的是在編譯時將參數重新排列成新的形式,然後在運行時進行評估。如果參數是(inc x )而不是一個println和一個字符串 - 在編譯時什麼是x?什麼時候應該發生? – 2015-03-04 09:06:52

回答

2

你給出的兩個例子做了完全不同的事情。首先是在宏展開時間處評估修改的s表達式,這幾乎肯定不是您想要的。

user=> (defmacro runargs-eval [args] 
     (eval (cons (first args) " buckaroo"))) 
#'user/runargs-eval 

user=> (macroexpand '(runargs-eval (println "hi there"))) 
    b u c k e r o o 
nil 

user=> (defmacro runargs [args] 
     `([email protected](cons (first args) " buckeroo"))) 
#'user/runargs 

user=> (macroexpand '(runargs (println "hi there"))) 
(println \space \b \u \c \k \e \r \o \o) 

沒有多大的差別,如果你只是簡單地評價一個s表達式,恰好包含調用您的宏,但如果你正在編譯代碼,使用您的宏(如的λ的主體),宏擴展發生在編譯時:

user=> (defn say-hello-eval [x] (runargs-eval (println x))) 
    b u c k e r o o 
#'user/say-hello-eval 

user=> (say-hello-eval "hi there") 
nil 

user=> (defn say-hello [x] (runargs (println x))) 
#'user/say-hello 

user=> (say-hello "hi there") 
    b u c k e r o o 
nil 

宏是簡單地接受一個未計算的表達式,並返回一個修改後的未計算的表達式的功能。如果你真的想評估表達式作爲宏展開的一部分,這將是一個非常不尋常的情況 - 通常你的宏將返回修改的表達式,並讓Clojure編譯器負責在適當的時候評估它。

第二個示例中的語法引用並不是真的必要 - 語法引用的一般用例是,當您將宏參數插入包含符號的模板化表達式中時,該符號應解析爲名稱空間中的某些該宏被定義。

(defmacro runargs [args] 
    (cons (first args) " buckeroo")) 
+0

釘住了它,謝謝。 – hawkeye 2015-03-07 10:45:03

相關問題