2017-09-23 43 views
1

我想通过编写一个宏来编写一个ACL模块,这个宏是为了检查宏中每个函数调用的结果,如果返回false,那么ACL将失败而不运行下面的函数调用。如果返回true并且还有函数调用来检查,那么检查以下内容直到最后一个。clojure宏unquote函数调用的拼接列表

(defmacro checks 
    [head & tail] 
    `(let [status# ~head] 
    (if (and (true? status#) 
       (seq '~tail)) 
     (checks [email protected]) 
     status#))) 

我会打电话给此宏是这样的:

(checks (module1 args) (module2 args))` 

但这将在宏定义失败(check [email protected])。问题是我想要Unquote拼接列表,但不调用列表中的每个函数。

+0

只是提示,不要使用反引号来格式化大段代码。通过突出显示它并按ctrl + k缩进四个空格。 – Carcigenicate

+0

宏和宏不会一样吗? –

+0

谢谢你们的评论。 –

回答

1

我已经找到了解决这个问题的办法:

(defmacro checks 
    [head & tail] 
    (let [sym (gensym)] 
    `(let [~sym ~head] 
     (if ~sym 
     ~(if tail 
      `(checks [email protected]) 
      sym) 
     ~sym)))) 

使用语法上外的(checks [email protected])形式再次引文结束。

+0

比[mine](https://stackoverflow.com/a/46378055/1562315)更清洁。它只是['和'](https://github.com/clojure/clojure/blob/clojure-1.9.0-alpha14/src/clj/clojure/core.clj#L834)的另一个名称,没有零元素。 – Thumbnail

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 [email protected]) 
     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。

+0

谢谢。该宏将生成不正确的递归调用。 –