我想根据某些功能的结果创建一个列表。在Java(我的背景)中,我会做类似的事情:
List<String> messages = ...
if(condition 1)
messages.add(message 1);
if(condition 2)
messages.add(message 2);
...
if(condition N)
messages.add(message N);
在clojure中,我想我需要使用let
创建一个列表,如下所示(只是虚拟示例):
(let [result
(vec
(if (= 1 1) "message1" "message2")
(if (= 1 0) "message3" "message4"))]
result)
我还检查了cond
,但我需要将这些元素附加到列表中,考虑所有的验证(并且在满足一个条件后cond
中断)。
我应该采用哪种方式来实现这一目标?
如果您希望像Java示例中那样有条件地添加它们,您可以使用cond->
,它不会短路:
(let [messages []]
(cond-> messages ; Conditionally thread through conj
(= 1 1) (conj "Message1")
(= 0 1) (conj "Message2")
(= 0 0) (conj "Message3")))
=> ["Message1" "Message3"]
如果你想像第二个例子那样有条件地添加一个或另一个,你可以使用普通的conj
和一些if
表达式:
(let [messages []]
(conj messages
(if (= 1 1) "Message1" "Message2")
(if (= 0 1) "Message3" "Message4")))
=> ["Message1" "Message4"]
而且我会注意到你原来的尝试几乎奏效了。而不是vec
,你可以使用vector
,或只是一个矢量文字:
(let [messages [(if (= 1 1) "Message1" "Message2")
(if (= 1 0) "Message3" "Message4")]]
messages)
=> ["Message1" "Message4"]
虽然,如果您还没有填充要填充的messages
,这只会是有益的。如果是这种情况,你必须使用concat
或into
:
(let [old-messages ["old stuff"]
messages [(if (= 1 1) "Message1" "Message2")
(if (= 1 0) "Message3" "Message4")]]
(into old-messages messages))
=> ["old stuff" "Message1" "Message4"]
看看cond->。
例如,您的Java示例可以写成:
(cond-> (some-fn-returning-messages)
(= 1 1) (conj "message1")
(= 1 2) (conj "message2")
...
(= 1 n) (conj "messagen"))
我看到几个答案指向cond->
宏,它似乎最符合您的要求,因为它最接近您问题中概述的样式。
根据你所拥有的条件数量,你的问题似乎是一个很好的候选人,只需使用qazxsw poi。
filter
如果你有一堆条件(函数),并且“或”将它们放在一起会很笨重,你可以使用(def nums (range 10))
(filter #(or (even? %) (= 7 %)) nums)
。
0-19中的数字是偶数,可被7整除,大于17,或者恰好等于1.愚蠢的例子我知道,只是想展示一个简单的用例。
some-fn
看起来每个人都有同样的想法!我用关键字做了我的:
(filter (some-fn
even?
#(zero? (mod % 7))
#(> % 17)
#(= 1 %))
(range 20))
如果你想要更多的力量,你可以使用(ns tst.demo.core
(:use tupelo.core demo.core tupelo.test))
(defn accum
[conds]
(cond-> [] ; append to the vector in order 1,2,3
(contains? conds :cond-1) (conj :msg-1)
(contains? conds :cond-2) (conj :msg-2)
(contains? conds :cond-3) (conj :msg-3)))
(dotest
(is= [:msg-1] (accum #{:cond-1}))
(is= [:msg-1 :msg-3] (accum #{:cond-1 :cond-3}))
(is= [:msg-1 :msg-2] (accum #{:cond-2 :cond-1}))
(is= [:msg-2 :msg-3] (accum #{:cond-2 :cond-3}))
(is= [:msg-1 :msg-2 :msg-3] (accum #{:cond-3 :cond-2 :cond-1 })) ; note sets are unsorted
)
cond-it->
。它通过条件和操作形式对目标值进行线程化,并使用特殊符号from the Tupelo library来显示线程值的放置位置。这个修改过的例子显示了第四个条件,其中“msg-3嫉妒msg-1”并且总是从结果中引出它:
it
不一定与您的用例相关,当然也不是主流解决方案,但偶尔我喜欢(ns tst.demo.core
(:use tupelo.core demo.core tupelo.test))
(defn accum
[conds]
(cond-it-> #{} ; accumulate result in a set
(contains? conds :cond-1) (conj it :msg-1)
(contains? conds :cond-2) (conj it :msg-2)
(contains? conds :cond-3) (conj it :msg-3)
(contains? it :msg-3) (disj it :msg-1) ; :msg-3 doesn't like :msg-1
))
; remember that sets are unsorted
(dotest
(is= #{:msg-1} (accum #{:cond-1}))
(is= #{:msg-3} (accum #{:cond-1 :cond-3}))
(is= #{:msg-1 :msg-2} (accum #{:cond-2 :cond-1}))
(is= #{:msg-2 :msg-3} (accum #{:cond-2 :cond-3}))
(is= #{:msg-2 :msg-3} (accum #{:cond-3 :cond-2 :cond-1 }))
)
的条件表达式:
cl-format
典型的情况是验证一段数据以产生错误列表。
我会构建一个将条件映射到消息的表:
(require '[clojure.pprint :refer [cl-format]])
(require '[clojure.data.generators :as g])
(cl-format nil
"~:[He~;She~] ~:[did~;did not~] ~:[thought about it~;care~]"
(g/boolean) (g/boolean) (g/boolean))
请注意,条件是函数。由于我们永远无法通过值正确识别函数,因此您可以使此表成为一对序列。
但是,您实现该表,我们所要做的就是收集适用的谓词的消息:
(def error->message-table
{condition1 message1
condition2 message2
...})
没有一个连贯的例子,很难更明确。
(defn messages [stuff]
(->> error->message-table
(filter (fn [pair] ((first pair) stuff)))
(map second)))
和filter
中的一流函数和打包控制结构为我们提供了简洁明了地表达算法的方法,将内容隔离成数据结构。