2011-04-03 55 views
15

如何讓Clojure宏充當函數,所以我可以將其作爲參數傳遞給它?我希望不得不以某種方式包裝它。將Clojure宏看作函數

我不希望包裝版本的行爲與原始宏完全一樣(名稱與值的調用不同),但在少數情況下這很重要。

回答

19

如果我正確理解你,你可以把它包裝在一個函數。

考慮這個(傻)實現平方功能的宏:

(defmacro square [x] 
    (list * x x)) 

直接把它當作一個ARG將無法正常工作,如你所知:

user=> (map square [1 2 3 4 5]) 
java.lang.Exception: Can't take value of a macro: #'user/square (NO_SOURCE_FILE:8) 

但其包裝在一個函數將做的伎倆:

user=> (map #(square %) [1 2 3 4 5]) 
(1 4 9 16 25) 

或者(更惡意地),你可以做另一個宏做一個更通用的包裝:

(defmacro make-fn [m] 
    `(fn [& args#] 
    (eval `(~'~m [email protected]#)))) 

user=> (map (make-fn square) [1 2 3 4 5]) 
(1 4 9 16 25) 

我會堅持正常的功能包裝,並跳過這最後的黑客! :)

+3

嗯,這不適用於可變宏: user =>(應用#(或%)[true false false]) java.lang.IllegalArgumentException:錯誤的參數數量(3)傳遞給:user $ eval3 $ fn(NO_SOURCE_FILE:0) – pauldoo 2011-04-05 07:32:18

+2

3分:(1)解決這個例子的最好方法是'(某種身份[true false false])',(2)如果你知道params的數量並且明確表示它, (應用#(或%1%2%3)[true false false]),(3)hacky'make-fn'工作:'(apply(make-fn or)[true false假])' – trptcolin 2011-04-05 13:06:20

5

有一個危險的,不推薦使用的宏,你不應該使用它。 :-P

http://clojure.github.com/clojure-contrib/apply-macro-api.html

+0

嗯,這給了我一個堆棧溢出,當我試圖用Clojure 1.2這個例子:(?應用宏或[真虛假])) – pauldoo 2011-04-05 07:50:36

+0

唉這是真的,問題是私有函數價差apply-macro ns:它是一個陳舊clojure.core的副本,如果你用實際版本替換它[黑客工程](https://gist.github.com/986321) – jneira 2011-05-23 06:29:39

+0

diff是好奇的rest [1]) - >(),(next [1]) - > nil。第一個與零?作爲防止謂詞停止遞歸引發了stackoverflow。 – jneira 2011-05-23 06:32:16

4

對於瘋狂的宏make-fn,下面的那個怎麼樣? 它應該工作得很好,並且希望更容易理解。

(defmacro make-fn [m] 
`(fn [& args#] 
    (eval 
     (cons '~m args#))))