在clojure
,apply
不能应用于宏.例如,(apply and [true false])
引发异常.我正在考虑以下解决方法:
(defmacro apply-macro[func args] `(~func ~@args))
Run Code Online (Sandbox Code Playgroud)
乍一看,它看起来效果很好:
(apply-macro and [true 5]); 5
(apply-macro and [true 5 0]); 0
(let [a 0] (apply-macro and [true a])); 0
Run Code Online (Sandbox Code Playgroud)
但是,当我向它传递一个指向向量的变量时,它就崩溃了.
(let [a [true]] (apply-macro and a)); java.lang.IllegalArgumentException:
;Don't know how to create ISeq from: clojure.lang.Symbol
Run Code Online (Sandbox Code Playgroud)
多么令人失望!!!!
知道怎么解决apply-macro
?
Ale*_*art 30
你没有.
宏在评估/编译期间而不是在运行时扩展,因此它们可以使用的唯一信息是传入的args,而不是args在运行时评估的内容.这就是文字向量起作用的原因,因为文字向量在编译时存在,但a
只是一个符号; 它只会在运行时评估为向量.
要使and
列表具有类似行为,请使用(every? identity coll)
.
要使or
列表具有类似行为,请使用(some identity coll)
.
mik*_*era 28
问题是它a
只是编译时的符号.因此,编译时宏无法查看它包含的内容并进行必要的扩展.因此,您需要使用eval在运行时扩展宏.
一种方法是将宏包装在一个调用eval的函数中,这可以通过这个方便的"函数化"宏来完成:
(defmacro functionize [macro]
`(fn [& args#] (eval (cons '~macro args#))))
(let [a [true]] (apply (functionize and) a))
=> true
Run Code Online (Sandbox Code Playgroud)
如果您愿意,还可以根据函数化定义apply-macro:
(defmacro apply-macro [macro args]
`(apply (functionize ~macro) ~args))
(let [a [true false]] (apply-macro and a))
=> false
Run Code Online (Sandbox Code Playgroud)
说完这一切之后,我仍然认为最好的办法就是在不需要宏时完全避免使用宏:它们会增加额外的复杂性,最好保留用于真正需要编译时代码生成的情况.在这种情况下,你不会:Alex Taggart的答案提供了一个很好的例子,说明如何在没有任何宏的情况下实现类似的目标,这在大多数情况下可能更合适.