6

当我使用 if-let like

(if-let [a 2 b nil] (+ a b))

我得到一个 IllegalArgumentException:

clojure.core/if-let requires exactly 2 forms in binding vector...

类似的当-让...

这不是我所期望的。If-let 可以尝试所有绑定并在其中一个失败时中断并评估 else 表达式。

在clojuredocs的评论中可以找到相同的投诉。我在这里找到了一个并没有真正令人满意的答案,因为张贴者似乎考虑到了相当于嵌套的 if-let 结构。

有什么理由限制 *-let 宏的绑定?

更新:似乎还不清楚,我对 if-let 的期望是:

  • 它应该按顺序评估所有绑定。
  • 当一切都成功时,它应该评估'then'-case。
  • 如果一个绑定失败,它应该立即中断并评估“其他”情况。
  • 在失败的情况下,绑定,即使是成功的绑定,也不应该在“else”表达式中可用
4

3 回答 3

3

试试这个:

(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))))

这是在行动:

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"
于 2013-04-26T04:15:07.240 回答
3

if-let并且let服务于不同的目的,if-let 不仅仅是一个更受限制的 let 版本。of instance 的if-let不同之处let在于该值仅绑定到 then 子句而不绑定 else。

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 旨在让您在绑定值只是为了测试和使用它的情况下更轻松。

于 2013-04-25T23:37:25.377 回答
1

试试这个。

(defmacro if-let
  ([bindings true-expr] `(if-lets ~bindings ~true-expr nil))
  ([绑定 true-expr false-expr]
    (条件
      (or (not (seq bindings)) (not (zero? (rem (count bindings) 2))))
        `(抛出(IllegalArgumentException。“if-lets 需要 2 个或多个 2 个表单在用户:1 的绑定向量中”))
      (seq (drop 2 bindings))
        `(if-let ~(vec (take 2 bindings))
                 (if-lets ~(vec (drop 2 bindings))
                          ~true-expr
                          ~false-expr)
                 ~false-expr)
      :别的
        `(if-let ~(vec 绑定)
                 ~true-expr
                 ~false-expr))))

这个宏通过了下面的这些测试。

(deftest ut-if-let
  (测试“if-lets 宏(正常情况)”
    (is (= 0 (if-lets [x 0] x)))
    (是 (= 0 (if-lets [x 0] x 1)))
    (is (= 1 (if-lets [x nil] x 1)))
    (is (= 0 (if-lets [x 0 yx] y)))
    (是 (= 0 (if-lets [x 0 yx] y 1)))
    (is (= 1 (if-lets [x nil yx] y 1)))
    (is (= 0 (if-lets [x 0 yxzy] z)))
    (is (= 0 (if-lets [x 0 yxzy] z 1)))
    (is (= 1 (if-lets [x nil yxzy] 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
  (测试“if-lets 宏(异常情况)”
    (is (= (try (if-lets [] true false) (catch Exception e (.getMessage e)))
        “if-lets 需要用户:1 中的绑定向量中的 2 种或 2 种形式的倍数”))
    (is (= (try (if-lets [x] true false) (catch Exception e (.getMessage e)))
        “if-lets 需要用户:1 中的绑定向量中的 2 种或 2 种形式的倍数”))
    (is (= (try (if-lets [x true y] true false) (catch Exception e (.getMessage e)))
        “if-lets 需要用户:1 中的绑定向量中的 2 种或 2 种形式的倍数”))
  )
)
于 2014-08-01T00:09:03.917 回答