在Clojure中,测试一个列表是否包含给定值的最佳方法是什么?
特别是,contains?
函数的行为目前让我感到困惑:
(contains? '(100 101 102) 101) => false
我显然可以编写一个简单的函数来遍历列表并测试相等性,但肯定有一种标准方法可以做到这一点吧?
在Clojure中,测试一个列表是否包含给定值的最佳方法是什么?
特别是,contains?
函数的行为目前让我感到困惑:
(contains? '(100 101 102) 101) => false
我显然可以编写一个简单的函数来遍历列表并测试相等性,但肯定有一种标准方法可以做到这一点吧?
使用集合和使用map类似,只需将其放入函数位置即可。它会评估集合中的值(为真)或nil
(为假)并返回结果:
(#{100 101 102} 101) ; 101
(#{100 101 102} 99) ; nil
如果你是在运行时检查一个相当大的向量/列表,你也可以使用set
函数:
; (def nums '(100 101 102))
((set nums) 101) ; 101
(defn which?
"Checks if any of elements is included in coll and says which one
was found as first. Coll can be map, list, vector and set"
[ coll & rest ]
(let [ncoll (if (map? coll) (keys coll) coll)]
(reduce
#(or %1 (first (filter (fn[a] (= a %2))
ncoll))) nil rest )))
示例用法(哪个?[1 2 3] 3)或(哪个?#{1 2 3} 4 5 3)
(defn in?
[needle coll]
(when (seq coll)
(or (= needle (first coll))
(recur needle (next coll)))))
(defn first-index
[needle coll]
(loop [index 0
needle needle
coll coll]
(when (seq coll)
(if (= needle (first coll))
index
(recur (inc index) needle (next coll))))))
some
- 请参阅clojure.core/some
文档。some
,例如:(defn in? [coll x] (if (some #{x} coll) true false))
if
的 true
和 false
?some
已经返回了类似于 true 和 false 的值。 - subsub.indexOf
。该函数返回集合中任何元素的索引,如果找不到该元素,则返回-1。(not= (.indexOf [1 2 3 4] 3) -1)
=> true
(defn member?
"I'm still amazed that Clojure does not provide a simple member function.
Returns true if `item` is a member of `series`, else nil."
[item series]
(and (some #(= item %) series) true))
另一个选项:
((set '(100 101 102)) 101)
(.contains '(100 101 102) 101)
发现有点晚了,但这就是我正在做的事情
(some (partial = 102) '(101 102 103))
这个目的有方便的函数在Tupelo库中。特别是,contains-elem?
、contains-key?
和contains-val?
函数非常有用。完整的文档在API文档中。
contains-elem?
是最通用的,适用于向量或任何其他clojure seq
:
(testing "vecs"
(let [coll (range 3)]
(isnt (contains-elem? coll -1))
(is (contains-elem? coll 0))
(is (contains-elem? coll 1))
(is (contains-elem? coll 2))
(isnt (contains-elem? coll 3))
(isnt (contains-elem? coll nil)))
(let [coll [ 1 :two "three" \4]]
(isnt (contains-elem? coll :no-way))
(isnt (contains-elem? coll nil))
(is (contains-elem? coll 1))
(is (contains-elem? coll :two))
(is (contains-elem? coll "three"))
(is (contains-elem? coll \4)))
(let [coll [:yes nil 3]]
(isnt (contains-elem? coll :no-way))
(is (contains-elem? coll :yes))
(is (contains-elem? coll nil))))
(testing "maps"
(let [coll {1 :two "three" \4}]
(isnt (contains-elem? coll nil ))
(isnt (contains-elem? coll [1 :no-way] ))
(is (contains-elem? coll [1 :two]))
(is (contains-elem? coll ["three" \4])))
(let [coll {1 nil "three" \4}]
(isnt (contains-elem? coll [nil 1] ))
(is (contains-elem? coll [1 nil] )))
(let [coll {nil 2 "three" \4}]
(isnt (contains-elem? coll [1 nil] ))
(is (contains-elem? coll [nil 2] ))))
搜索一个集合也很简单:
(testing "sets"
(let [coll #{1 :two "three" \4}]
(isnt (contains-elem? coll :no-way))
(is (contains-elem? coll 1))
(is (contains-elem? coll :two))
(is (contains-elem? coll "three"))
(is (contains-elem? coll \4)))
(let [coll #{:yes nil}]
(isnt (contains-elem? coll :no-way))
(is (contains-elem? coll :yes))
(is (contains-elem? coll nil)))))
对于地图和集合,使用contains-key?
查找地图条目或集合元素更简单(也更有效):
(deftest t-contains-key?
(is (contains-key? {:a 1 :b 2} :a))
(is (contains-key? {:a 1 :b 2} :b))
(isnt (contains-key? {:a 1 :b 2} :x))
(isnt (contains-key? {:a 1 :b 2} :c))
(isnt (contains-key? {:a 1 :b 2} 1))
(isnt (contains-key? {:a 1 :b 2} 2))
(is (contains-key? {:a 1 nil 2} nil))
(isnt (contains-key? {:a 1 :b nil} nil))
(isnt (contains-key? {:a 1 :b 2} nil))
(is (contains-key? #{:a 1 :b 2} :a))
(is (contains-key? #{:a 1 :b 2} :b))
(is (contains-key? #{:a 1 :b 2} 1))
(is (contains-key? #{:a 1 :b 2} 2))
(isnt (contains-key? #{:a 1 :b 2} :x))
(isnt (contains-key? #{:a 1 :b 2} :c))
(is (contains-key? #{:a 5 nil "hello"} nil))
(isnt (contains-key? #{:a 5 :doh! "hello"} nil))
(throws? (contains-key? [:a 1 :b 2] :a))
(throws? (contains-key? [:a 1 :b 2] 1)))
对于地图,您还可以使用contains-val?
搜索值:
(deftest t-contains-val?
(is (contains-val? {:a 1 :b 2} 1))
(is (contains-val? {:a 1 :b 2} 2))
(isnt (contains-val? {:a 1 :b 2} 0))
(isnt (contains-val? {:a 1 :b 2} 3))
(isnt (contains-val? {:a 1 :b 2} :a))
(isnt (contains-val? {:a 1 :b 2} :b))
(is (contains-val? {:a 1 :b nil} nil))
(isnt (contains-val? {:a 1 nil 2} nil))
(isnt (contains-val? {:a 1 :b 2} nil))
(throws? (contains-val? [:a 1 :b 2] 1))
(throws? (contains-val? #{:a 1 :b 2} 1)))
从测试中可以看出,每个函数在搜索 nil
值时都能正常工作。