11

假设我有一张这种形式的地图:

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

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

4

5 回答 5

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)

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

于 2012-07-26T18:07:03.100 回答
6

您可以使用地图解构——Clojure 的一个有用功能。and这也利用了短路的事实,并且在第二个映射中找不到的第一个映射中的任何键都会得到nil一个虚假值:

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

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

于 2012-07-26T17:46:43.153 回答
1

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

user> (not-any? nil? [(m :a) (m :b)])
true
user> (not-any? nil? [(m :a) (m :b) (m :d)])
false
user> 
于 2012-07-26T17:37:48.320 回答
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)
于 2012-07-26T17:57:15.820 回答
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"))
于 2016-06-15T09:01:03.143 回答