如何在Clojure宏中递归打破条件?

时间:2018-08-17 10:05:29

标签: clojure

(defmacro block [ctx & expr]
    `(let [~@(mapcat (fn [[k v]] [k `~v]) ctx)]
         ~@expr
     ))

(defn action1 [] (print "action1") (rand-nth [true false]))
(defn action2 [] (print "action2") (rand-nth [true false]))

( block  { __blockaddrabsolute "1_1" __blockaddr "1_1"}
      ( block  {typeofparent "ummutate"  __nodeid "c21f80" __blockaddr "1_1_1"} ( action1 ))
      ( block  {__blockaddrabsolute "1_1_2" __nodeid "c60590" __blockaddr "1_1_2"} ( action2 ))
      ( block  {__blockaddrabsolute "1_1_3" __nodeid "c60595" __blockaddr "1_1_3"} ( action1 )) 
      ( block  {__blockaddrabsolute "1_1_4" __nodeid "c60596" __blockaddr "1_1_4"} ( action2 ))
 "end" )

如果任何操作返回false,我想中断宏评估的执行。

预期输出:

action1 true
action2 true
action1 false

1 个答案:

答案 0 :(得分:2)

您所需的短路行为可通过if / when形式获得,因此我们可以使用宏将 body 中的一系列形式转换为嵌套的{ {1}}表格:

when

然后,如果我们(defmacro block [bindings & body] (let [whens (reduce (fn [acc elem] `(when ~elem ~acc)) (last body) (reverse (butlast body)))] `(let [~@(mapcat (fn [[k v]] [k `~v]) bindings)] ~whens))) 您的示例macroexpand表格,我们将得到此格式(出于可读性考虑而对其进行了重新格式化):

block

因为您的(let* [__blockaddrabsolute "1_1" __blockaddr "1_1"] (when (block {typeofparent "ummutate", __nodeid "c21f80", __blockaddr "1_1_1"} (action1)) (when (block {__blockaddrabsolute "1_1_2", __nodeid "c60590", __blockaddr "1_1_2"} (action2)) (when (block {__blockaddrabsolute "1_1_3", __nodeid "c60595", __blockaddr "1_1_3"} (action1)) (when (block {__blockaddrabsolute "1_1_4", __nodeid "c60596", __blockaddr "1_1_4"} (action2)) "end"))))) / action1函数返回随机布尔值,您将获得不同的结果,但是您确实获得了所需的短路行为。如果任何嵌套表单未通过action2测试,则最终结果将为nil。

我会考虑通过引入更集中,通常更有用的类似when的宏来重构此宏,当它的任何内部形式不真实且根本不关心绑定时,它就会短路。然后将do用于内部绑定:

let