1
(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
4

1 回答 1

2

你想要的短路行为是通过if/ forms来实现的,所以我们可以使用宏将bodywhen中的一系列表单转化为嵌套表单: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函数返回随机布尔值,您将获得不同的结果,但您确实获得了所需的短路行为。如果任何嵌套表单未通过when测试,则最终结果将为 nil。

我会考虑通过引入一个更集中、通常有用do的宏来重构它,当它的任何内部形式不真实并且根本不关心绑定时,它会短路。然后let用于您的内部绑定:

(defmacro do-when [x & xs]
  (if xs
    `(when ~x (do-when ~@xs))
    `~x))

(do-when
  (let [x 1 y 2] (println "step 1") (= x (dec y)))
  (rand-nth [true false])
  "randomly successful result")
于 2018-08-17T12:52:27.420 回答