clojure宏unquote拼接函数调用列表

时间:2017-09-23 02:01:38

标签: clojure macros

我想通过编写宏来编写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 列表但不调用列表中的每个函数。

2 个答案:

答案 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。