mik*_*era 154 clojure data-structures
在Clojure中测试列表是否包含给定值的最佳方法是什么?
特别是,这种行为contains?
目前令我感到困惑:
(contains? '(100 101 102) 101) => false
Run Code Online (Sandbox Code Playgroud)
我显然可以编写一个简单的函数来遍历列表并测试相等性,但肯定有一种标准的方法可以做到这一点吗?
Mic*_*zyk 197
啊,contains?
...据说是五大常见问题之一:Clojure.
它不检查一个集合是否包含一个值; 它检查是否一个项目可以与检索get
或者,换句话说,一个集合是否包含键.这使得套(可看作使得键和值之间没有区别),地图(所以感觉(contains? {:foo 1} :foo)
是true
)和载体(但要注意(contains? [:foo :bar] 0)
是true
,由于按键这里有指标,问题中的载体并"包含"了索引0
!).
为了增加混乱,在没有意义的情况下 更新:在Clojure中≥1.5 contains?
,它只是返回false
; 这是发生在(contains? :foo 1)
和也 (contains? '(100 101 102) 101)
.contains?
递给不支持预期的"关键成员"测试的类型的对象时抛出.
做你想做的事的正确方法如下:
; most of the time this works
(some #{101} '(100 101 102))
Run Code Online (Sandbox Code Playgroud)
当搜索一堆物品中的一个时,你可以使用更大的一组; 搜索false
/时nil
,你可以使用false?
/ nil?
- 因为(#{x} x)
返回x
,因此(#{nil} nil)
是nil
; 多个项目的一个搜索时,其中一些可能是false
或者nil
,你可以使用
(some (zipmap [...the items...] (repeat true)) the-collection)
Run Code Online (Sandbox Code Playgroud)
(请注意,这些项目可以传递给zipmap
任何类型的集合.)
j-g*_*tus 125
这是我用于同一目的的标准工具:
(defn in?
"true if coll contains elm"
[coll elm]
(some #(= elm %) coll))
Run Code Online (Sandbox Code Playgroud)
Giu*_*eon 14
我知道我有点晚了,但是怎么样:
(contains? (set '(101 102 103)) 102)
Run Code Online (Sandbox Code Playgroud)
最后在clojure 1.4输出真的:)
jam*_*qiu 12
(not= -1 (.indexOf '(101 102 103) 102))
Run Code Online (Sandbox Code Playgroud)
有效,但下面更好:
(some #(= 102 %) '(101 102 103))
Run Code Online (Sandbox Code Playgroud)
Yur*_*nov 12
您始终可以使用.methodName语法调用java方法.
(.contains [100 101 102] 101) => true
Run Code Online (Sandbox Code Playgroud)
对于它的价值,这是我对列表的包含函数的简单实现:
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (first s) value) true (recur (rest s) value))
false)))
Run Code Online (Sandbox Code Playgroud)
如果您有一个向量或列表并想要检查其中是否包含值,您会发现它contains?
不起作用.Michał已经解释了原因.
; does not work as you might expect
(contains? [:a :b :c] :b) ; = false
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您可以尝试以下四种方法:
考虑一下你是否真的需要一个矢量或列表.如果你改用一套,那就行了contains?
.
(contains? #{:a :b :c} :b) ; = true
Run Code Online (Sandbox Code Playgroud)使用some
,将目标包装在一个集合中,如下所示:
(some #{:b} [:a :b :c]) ; = :b, which is truthy
Run Code Online (Sandbox Code Playgroud)如果要搜索虚假值(false
或nil
),则设置为功能的快捷方式将不起作用.
; will not work
(some #{false} [true false true]) ; = nil
Run Code Online (Sandbox Code Playgroud)
在这些情况下,您应该为该值使用内置谓词函数,false?
或者nil?
:
(some false? [true false true]) ; = true
Run Code Online (Sandbox Code Playgroud)如果您需要进行大量搜索,请为其编写一个函数:
(defn seq-contains? [coll target] (some #(= target %) coll))
(seq-contains? [true false true] false) ; = true
Run Code Online (Sandbox Code Playgroud)另外,请参阅Michał的答案,了解如何检查序列中是否包含多个目标.
这是我用于此目的的标准实用程序中的快速功能:
(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))))
Run Code Online (Sandbox Code Playgroud)
这是经典的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)))
Run Code Online (Sandbox Code Playgroud)