我想通过编写宏来编写ACL模块,这个宏是检查宏中每个函数调用的结果,如果一个返回false,那么ACL将失败而不运行以下函数调用。如果一个返回true并且仍有函数调用要检查,则检查以下内容直到最后一个。
(defmacro checks
[head & tail]
`(let [status# ~head]
(if (and (true? status#)
(seq '~tail))
(checks ~@tail)
status#)))
我会这样称呼这个宏:
(checks (module1 args) (module2 args))`
但宏定义中的(check ~@tail)
会失败。问题是我想要 Unquote Splicing 列表但不调用列表中的每个函数。
答案 0 :(得分:1)
我找到了解决这个问题的方法:
(defmacro checks
[head & tail]
(let [sym (gensym)]
`(let [~sym ~head]
(if ~sym
~(if tail
`(checks ~@tail)
sym)
~sym))))
在(checks ~@tail)
表单外部再次使用语法unquote。
答案 1 :(得分:0)
问题在于,即使没有参数,您也会为宏的递归调用生成代码。
(macroexpand-1 '(checks 1))
=>
(clojure.core/let
[status__1973__auto__ 1]
(if
(clojure.core/and (clojure.core/true? status__1973__auto__) (clojure.core/seq (quote nil)))
(user/checks)
status__1973__auto__))
内部宏调用失败,因为它至少需要一个参数。无论你向宏提供多少个参数,它的扩展最终都会产生失败的调用。
在生成对tail
的递归调用之前,您需要确定checks
中是否有任何内容:
(defmacro checks
[head & tail]
(if-not tail
head
`(let [status# ~head]
(if (true? status#)
(checks ~@tail)
status#))))
现在
(macroexpand-1 '(checks 1))
=> 1
(checks 1)
=> 1
和
(macroexpand-1 '(checks 1 2 3))
=>
(clojure.core/let
[status__2010__auto__ 1]
(if (clojure.core/true? status__2010__auto__) (user/checks 2 3) status__2010__auto__))
此外
(checks 1 2 3)
=> 1
(checks true true 3)
=> 3
正如@ PiotrekBzdyl的评论所暗示的那样,这只是一个and
宏,它将true
以外的任何内容视为false。