Data structures 测试列表是否包含Clojure中的特定值
在Clojure中测试列表是否包含给定值的最佳方法是什么 特别是,Data structures 测试列表是否包含Clojure中的特定值,data-structures,clojure,Data Structures,Clojure,在Clojure中测试列表是否包含给定值的最佳方法是什么 特别是,包含?的行为目前让我感到困惑: (contains? '(100 101 102) 101) => false 很明显,我可以编写一个简单的函数来遍历列表并测试是否相等,但肯定有一种标准的方法可以做到这一点?啊,包含?。。。应该是五大常见问题之一:Clojure 它不检查集合是否包含值;它检查是否可以使用get检索项,或者换句话说,集合是否包含键。这对于集合(可以认为不区分键和值)、映射(因此(包含?{:foo 1}:fo
包含?
的行为目前让我感到困惑:
(contains? '(100 101 102) 101) => false
很明显,我可以编写一个简单的函数来遍历列表并测试是否相等,但肯定有一种标准的方法可以做到这一点?啊,
包含?
。。。应该是五大常见问题之一:Clojure
它不检查集合是否包含值;它检查是否可以使用get
检索项,或者换句话说,集合是否包含键。这对于集合(可以认为不区分键和值)、映射(因此(包含?{:foo 1}:foo)
是true
)和向量(但是请注意(包含?[:foo:bar]0)
是true
,因为这里的键是索引,而所讨论的向量确实“包含”索引0
!)
如果调用contains?
没有意义,只需返回false
;这是在(contains?:foo 1)
和(contains?)(100 101 102)101)
中发生的情况。更新:在Clojure≥ 1.5包含?
当递给不支持预期“密钥成员资格”测试类型的对象时抛出
正确的方法如下所示:
; most of the time this works
(some #{101} '(100 101 102))
(some #{:b} [:a :b :c]) ; = :b, which is truthy
在搜索一组项目中的一个时,可以使用更大的集合;在搜索false
/nil
时,可以使用false?
/nil?
——因为({x}x)
返回x
,因此({nil}nil)
是nil
;当搜索多个项目中的一个时,其中一些可能是false
或nil
,您可以使用
(some (zipmap [...the items...] (repeat true)) the-collection)
(请注意,在任何类型的集合中,项目都可以传递到
zipmap
)我使用的标准实用程序中有一个快速功能:
(defn seq-contains?
"Determine whether a sequence contains a given item"
[sequence item]
(if (empty? sequence)
false
(reduce #(or %1 %2) (map #(= %1 item) sequence))))
值得一提的是,这是我对列表包含函数的简单实现:
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (first s) value) true (recur (rest s) value))
false)))
以下是我的标准util,用于相同的目的:
(defn in?
"true if coll contains elm"
[coll elm]
(some #(= elm %) coll))
我是建立在j-g-faustus的“列表包含?”。它现在接受任意数量的参数
(defn list-contains?
([collection value]
(let [sequence (seq collection)]
(if sequence (some #(= value %) sequence))))
([collection value & next]
(if (list-contains? collection value) (apply list-contains? collection next))))
我知道我有点晚了,但是:
(contains? (set '(101 102 103)) 102)
最后在clojure 1.4中输出true:)
工作正常,但以下更好:
(some #(= 102 %) '(101 102 103))
推荐的方法是将
some
与集合一起使用-请参阅clojure.core/some
的文档
然后,您可以在真/假谓词中使用some
,例如
(defn in? [coll x] (if (some #{x} coll) true false))
如果您有一个向量或列表,并且希望检查其中是否包含值,您将发现这不起作用。 米夏已经有了 在这种情况下,您可以尝试以下四种方法:
包含?
将起作用
(contains? #{:a :b :c} :b) ; = true
; most of the time this works
(some #{101} '(100 101 102))
(some #{:b} [:a :b :c]) ; = :b, which is truthy
false
或nil
),则设置为函数快捷方式将不起作用
在这些情况下,应对该值使用内置谓词函数,或:
(defn seq-contains? [coll target] (some #(= target %) coll))
(seq-contains? [true false true] false) ; = true
此外,请参阅以了解检查序列中是否包含多个目标中的任何一个的方法。以下是经典的Lisp解决方案:
(defn member? [list elt]
"True if list contains at least one instance of elt"
(cond
(empty? list) false
(= (first list) elt) true
true (recur (rest list) elt)))
(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))
它就像使用一个集合一样简单-类似于映射,您可以将它放在函数位置。如果在集合中(这是真的)或
nil
(这是假的),则其计算值为:
如果您正在检查运行时之前无法获得的大小合理的向量/列表,您还可以使用set
功能:
; (def nums '(100 101 102))
((set nums) 101) ; 101
您始终可以使用.methodName语法调用java方法
(.contains [100 101 102] 101) => true
由于Clojure是基于Java构建的,因此您可以轻松地调用
.indexOf
Java函数。此函数返回集合中任何元素的索引,如果找不到此元素,则返回-1
利用这一点,我们可以简单地说:
(not= (.indexOf [1 2 3 4] 3) -1)
=> true
“推荐”解决方案的问题是,当您要查找的值为“nil”时,它会中断。我更喜欢这个解决方案:
(defn member? [list elt]
"True if list contains at least one instance of elt"
(cond
(empty? list) false
(= (first list) elt) true
true (recur (rest list) elt)))
(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))
示例用法(which?[1 2 3]3)或(which?{1 2 3}4 5 3)有用于此目的的方便函数。特别是,函数
包含elem?
、包含key?
和包含val?
非常有用。有完整的文件
包含元素?
是最通用的,适用于向量或任何其他clojureseq
:
(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))))
这里我们看到,对于整数范围或混合向量,包含elem?
对于集合中的现有元素和非现有元素都能正常工作。对于映射,我们还可以搜索任何键值对(表示为len-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)))
对于地图,您还可以使用包含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
值时,这些函数中的每一个都能正常工作。另一个选项:
((set '(100 101 102)) 101)
使用java.util.Collection#contains():
真奇怪,是吗?必须是Clojure中最容易引起误解的函数:)希望Clojure 1.3能将其重命名为contains key?或者类似的。我想这已经被说了好几次了。包含?不会改变。请看这里:@kotarak谢谢你的链接!实际上,我同意Rich在这里对contains的使用?尽管我认为应该修改名称,以便在应用于列表或序列时抛出错误谢谢Michal-你是一个字体o
(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)))))
(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)))
(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)))
((set '(100 101 102)) 101)
(.contains '(100 101 102) 101)