【问题标题】:Why don't when-let and if-let support multiple bindings by default?为什么 when-let 和 if-let 默认不支持多个绑定?
【发布时间】:2012-07-26 18:52:54
【问题描述】:

为什么when-letif-let默认不支持多个绑定?

所以:

(when-let [a ...
           b ...]
  (+ a b))

...而不是:

(when-let [a ...
  (when-let [b ...
    (+ a b)))

我知道我可以编写自己的宏或使用 monad(如此处所述:http://inclojurewetrust.blogspot.com/2010/12/when-let-maybe.html)。

【问题讨论】:

  • 我写了一个你要求的函数。希望对你有帮助!

标签: clojure


【解决方案1】:

因为(至少对于if-let),如何处理“其他”情况并不明显。

至少,在Better way to nest if-let in clojure 的激励下,我开始编写一个宏来实现这一点。给定

(if-let* [a ...
          b ...]
  action
  other)

它会生成

(if-let [a ...]
  (if-let [b ...]
    action
    ?))

我不清楚如何继续(“else”有两个地方)。

您可以说任何失败都应该有一个替代方案,或者when-let 没有一个替代方案,但是如果任何测试状态发生突变,那么事情仍然会变得一团糟。

简而言之,它比我预期的要复杂一些,所以我想当前的方法避免了必须调用解决方案应该是什么。

另一种说法:您假设if-let 应该像let 一样嵌套。更好的模型可能是 cond,它不是“嵌套 if”,而是更多“替代 if”,因此不太适合范围......或者,另一种说法:if没有更好地处理这种情况。

【讨论】:

  • if-let* 宏也可以很好地回答我的问题;)
  • 但它不存在 - 我看不到处理“其他”部分的好方法。
  • 你不能用两次other吗? (if-let [a ...] (if-let [b ...] action other) other))?
  • @andrewcooke “你可以说应该只有一个选择”:完全正确。当 all 表单评估为真时执行action,当至少一个为 NIL 时执行other(另见whereas)。不过,我不明白您为什么担心可变性。
【解决方案2】:

这是when-let*:

(defmacro when-let*
  "Multiple binding version of when-let"
  [bindings & body]
  (if (seq bindings)
    `(when-let [~(first bindings) ~(second bindings)]
       (when-let* ~(vec (drop 2 bindings)) ~@body))
    `(do ~@body)))

用法:

user=> (when-let* [a 1 b 2 c 3]
                (println "yeah!")
                a)
;;=>yeah!
;;=>1


user=> (when-let* [a 1 b nil c 3]
                (println "damn! b is nil")
                a)
;;=>nil


这里是 if-let*:

(defmacro if-let*
  "Multiple binding version of if-let"
  ([bindings then]
   `(if-let* ~bindings ~then nil))
  ([bindings then else]
   (if (seq bindings)
     `(if-let [~(first bindings) ~(second bindings)]
        (if-let* ~(vec (drop 2 bindings)) ~then ~else)
        ~else)
     then)))

用法:

user=> (if-let* [a 1 
                 b 2 
                 c (+ a b)]
              c
              :some-val)
;;=> 3

user=> (if-let* [a 1 b "Damn!" c nil]
              a
              :some-val)
;;=> :some-val

编辑:原来绑定不应该以 else 形式泄露。

【讨论】:

【解决方案3】:

如果您使用cats,那么您可能会发现有一个mlet 函数很有用:

(use 'cats.builtin)
(require '[cats.core :as m])
(require '[cats.monad.maybe :as maybe])

(m/mlet [x (maybe/just 42)
         y nil]
  (m/return (+ x y)))
;; => nil

如您所见,mlet 在遇到 nil 值时会短路。

(来自第 6.5.1 节无)

【讨论】:

    猜你喜欢
    • 2019-11-30
    • 2014-03-21
    • 2022-01-08
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 2015-02-16
    • 2013-09-22
    • 1970-01-01
    相关资源
    最近更新 更多