Clojure: Why does if-let only allow 2 forms in the binding vector?
当我使用if-let like
1 | (if-let [a 2 b nil] (+ a b)) |
我收到一个IllegalArgumentException:
1 | clojure.core/if-let requires exactly 2 forms in binding vector... |
类似的时候让......
这不是我期望的。 if-let可以尝试所有绑定并在一个绑定失败时中断并评估else表达式。
在clojuredocs的评论中可以找到相同的投诉。我在这里找到了一个并不能真正满足的答案,因为发帖人似乎已经想到了嵌套的if-let-structure。
有什么理由限制* -let宏的绑定?
更新:
似乎还不清楚,我对if-let的期望是:
- 它应该按顺序评估所有绑定。
- 当一切都成功后,它应该评估'then \\'情况。
- 如果一个绑定失败,它应该立即中断并评估" else"情况。
- 如果失败,即使在\\'else \\'表达式中,即使是成功的绑定也不应使用
尝试一下:
1 2 3 4 5 6 7 | (defmacro if-let-multi ([bindings then-exp] (let [values (take-nth 2 (rest bindings))] `(if (and ~@values) (let ~bindings ~then-exp) false))) ([bindings then-exp else-exp] (let [values (take-nth 2 (rest bindings))] `(if (and ~@values) (let ~bindings ~then-exp) ~else-exp)))) |
这里正在起作用:
1 2 3 4 5 6 | user> (if-let-multi [a 2 b nil] (+ a b)) false user> (if-let-multi [a 2 b 3] (+ a b)) 5 user> (if-let-multi [a 2 b nil] (+ a b)"NO WAY") "NO WAY" |
1 2 3 4 5 6 | user> (if-let [ans (+ 1 2 3)] ans :foo) 6 user> (if-let [ans (+ 1 2 3)] ans ans) CompilerException java.lang.RuntimeException: Unable to resolve symbol: ans in this context, compiling:(NO_SOURCE_PATH:1) user> (let [ans (+ 1 2 3)] ans ans) 6 |
if-let旨在使绑定值只是为了测试和使用它时使生活更轻松。
尝试一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (defmacro if-lets ([bindings true-expr] `(if-lets ~bindings ~true-expr nil)) ([bindings true-expr false-expr] (cond (or (not (seq bindings)) (not (zero? (rem (count bindings) 2)))) `(throw (IllegalArgumentException."if-lets requires 2 or multiple of 2 forms in binding vector in user:1")) (seq (drop 2 bindings)) `(if-let ~(vec (take 2 bindings)) (if-lets ~(vec (drop 2 bindings)) ~true-expr ~false-expr) ~false-expr) :else `(if-let ~(vec bindings) ~true-expr ~false-expr)))) |
此宏通过了以下这些测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | (deftest ut-if-lets (testing"if-lets macro (normal cases)" (is (= 0 (if-lets [x 0] x))) (is (= 0 (if-lets [x 0] x 1))) (is (= 1 (if-lets [x nil] x 1))) (is (= 0 (if-lets [x 0 y x] y))) (is (= 0 (if-lets [x 0 y x] y 1))) (is (= 1 (if-lets [x nil y x] y 1))) (is (= 0 (if-lets [x 0 y x z y] z))) (is (= 0 (if-lets [x 0 y x z y] z 1))) (is (= 1 (if-lets [x nil y x z y] y 1))) (is (= true (if-lets [x true] true false))) (is (= false (if-lets [x false] true false))) (is (= true (if-lets [x true y true] true false))) (is (= false (if-lets [x false y true] true false))) (is (= false (if-lets [x true y false] true false))) (is (= true (if-lets [x true y true z true] true false))) (is (= false (if-lets [x false y true z true] true false))) (is (= false (if-lets [x true y false z true] true false))) (is (= false (if-lets [x true y true z false] true false))) ) ) (deftest ut-if-lets-ab (testing"if-lets macro (abnormal cases)" (is (= (try (if-lets [] true false) (catch Exception e (.getMessage e))) "if-lets requires 2 or multiple of 2 forms in binding vector in user:1")) (is (= (try (if-lets [x] true false) (catch Exception e (.getMessage e))) "if-lets requires 2 or multiple of 2 forms in binding vector in user:1")) (is (= (try (if-lets [x true y] true false) (catch Exception e (.getMessage e))) "if-lets requires 2 or multiple of 2 forms in binding vector in user:1")) ) ) |