在clojure中嵌套if-let的更好方法

时间:2012-07-26 16:29:01

标签: if-statement clojure

说我有这张表格的地图:

(def m {:a "A" :b "B"})

如果:a:b都不是零,我想做点什么,我可以这样做:

(if-let [a (:a m)]
  (if-let [b (:b m)]
      ... etc ))

(if (and (:a m) (:b m))
  (let [{a :a b :b} m]
      ... etc ))

甚至

(if (every? m [:a :b])
  (let [{a :a b :b} m]
      ... etc ))

是否有更简洁(即一线)的方法来实现这一目标?

5 个答案:

答案 0 :(得分:7)

我认为这里可能需要一个宏来创建你想要的行为。我从来没有写过(但是)但是下面的表示向我暗示这可能相当简单:

(let [{:keys [a b]} m]
   (when (every? identity [a b])
      (println (str "Processing " a " and " b))))

使用:keys形式的解构绑定和every?使得键的向量的单个规范能够进行解构和检查,并且绑定的本地可用于以下代码块中。

这可以用来制作像(when-every? [keys coll] code-with-bindings)

这样的宏

我可以用宏代码更新这个答案,如果我可以花点时间弄清楚如何去做。

答案 1 :(得分:6)

你可以使用map destructuring - Clojure的一个有用功能。这也利用了and短路的事实,并且第二张地图中未找到的第一张地图中的任何键都获得nil,这是一个假值:

(let [{a :a b :b} {:a 1 :b "blah"}] 
  (and a b (op a b)))

好的,所以它是两条行而不是一行.......这也不区分nil和其他假值。

答案 2 :(得分:1)

not-any?是一个很好的捷径:

user> (not-any? nil? [(m :a) (m :b)])
true
user> (not-any? nil? [(m :a) (m :b) (m :d)])
false
user> 

答案 3 :(得分:1)

如果密钥具有非零值,或者您是否需要返回非零键或值,我不太确定您要执行的操作。所以,我刚刚解决它,因为返回了非零键。

作为最终解决方案的一部分,您将使用以下内容作为中间步骤。

我正在展示我使用的所有步骤,不是迂腐,而是提供完整的答案。名称空间为repl-test。它有一个主要与它相关。

repl-test.core=> (def m {:a "A" :b "B" :c nil})
#'repl-test.core/m

repl-test.core=> (keys m)
(:a :c :b)

然后最后:

; Check key's value to determine what is filtered through.
repl-test.core=> (filter #(if-not (nil? (%1 m)) (%1 m)) (keys m) )
(:a :b)

答案 4 :(得分:1)

顺便说一句,我发现了一个丑陋的单行,这是有效的,因为and返回其参数列表中的最后一个东西,如果它们都是真的:

(if-let [[a b] (and (:a m) (:b m) [(:a m)(:b m)])]
    (println "neither " a " nor " b " is falsey")
    (println "at least one of " a " or " b " is falsey"))