2011-04-03 53 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#))))