测试列表是否包含Clojure中的特定值

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!).

为了增加混乱,在没有意义的情况下contains?,它只是返回false; 这是发生在(contains? :foo 1) 和也 (contains? '(100 101 102) 101). 更新:在Clojure中≥1.5 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任何类型的集合.)

  • @DavidJames:如果您正在检查是否存在"false"或"nil",则它不起作用 - 请参阅以下段落.另外,在Clojure 1.5-RC1中,`contains?`在给出非键控集合作为参数时抛出异常.我想我会在最终版本发布时编辑这个答案. (7认同)
  • 正如Michal所说 - 核心中已经有了一个能够满足你的需求的功能:一些. (4认同)
  • 上面,Michal评论说`(一些#{101}'(100 101 102))`说"大部分时间都有效".说它始终有效是不公平的?我正在使用Clojure 1.4,文档使用了这种示例.它对我有用并且有意义.是否有某种特殊情况不起作用? (2认同)
  • @jgomo3你可以使用包含吗?在一组上测试成员资格。你不能做的是在地图或列表上使用它,因为这样做不再是 O(1),而是变成 O(n)。包含?意味着是 O(1),这就是为什么在这些情况下您需要进行自己的线性搜索。 (2认同)

j-g*_*tus 125

这是我用于同一目的的标准工具:

(defn in? 
  "true if coll contains elm"
  [coll elm]  
  (some #(= elm %) coll))
Run Code Online (Sandbox Code Playgroud)

  • 这是最简单,最安全的解决方案,因为它还可以处理像'nil`和`false`这样的虚假值.现在为什么这不是clojure/core的一部分? (35认同)
  • @nha你可以这样做,是的.这里没关系:因为我们没有在体内使用函数`seq`,所以与同名参数没有冲突.但如果您认为重命名会使其更容易理解,请随时编辑答案. (3认同)
  • `seq`也许可以重命名为`coll`,以避免与函数`seq`混淆? (2认同)
  • 值得注意的是,如果您不必担心 `nil` 或 `false`,这可能比 `(boolean (some #{elm} coll))` 慢 3-4 倍。 (2认同)
  • @AviFlax 我在考虑 https://clojure.org/guides/threading_macros,它说“按照惯例,对序列进行操作的核心函数期望序列作为它们的最后一个参数。因此,包含 map、filter、remove、reduce 的管道, into 等通常调用 ->> 宏。” 但我想约定更多的是关于对序列和返回序列进行操作的函数。 (2认同)

Giu*_*eon 14

我知道我有点晚了,但是怎么样:

(contains? (set '(101 102 103)) 102)
Run Code Online (Sandbox Code Playgroud)

最后在clojure 1.4输出真的:)

  • 这样做的缺点是需要将原始列表''(101 102 103)`转换为一组. (3认同)
  • `(set'(101 102 103))`与`%{101 102 103}`相同.所以你的答案可以写成`(包含?#{101 102 103} 102)`. (2认同)

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)

  • 恕我直言,这是最好的答案.太糟糕的clojure包含?是如此令人困惑的命名. (3认同)
  • 尊贵的 Qc Na 上师与他的学生 Anton 一起散步。当 Anton 告诉他关于“包含?”的一些初学者问题时,Qc Na 用 Bô 打了他并说:“愚蠢的学生!你必须意识到没有勺子。_下面都是 Java!_使用点表示法。 ”。那一刻,安东恍然大悟。 (3认同)

mik*_*era 7

对于它的价值,这是我对列表的包含函数的简单实现:

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


Ror*_*ane 6

如果您有一个向量或列表并想要检查其中是否包含,您会发现它contains?不起作用.Michał已经解释了原因.

; does not work as you might expect
(contains? [:a :b :c] :b) ; = false
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您可以尝试以下四种方法:

  1. 考虑一下你是否真的需要一个矢量或列表.如果你改用一套,那就行contains?.

    (contains? #{:a :b :c} :b) ; = true
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用some,将目标包装在一个集合中,如下所示:

    (some #{:b} [:a :b :c]) ; = :b, which is truthy
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果要搜索虚假值(falsenil),则设置为功能的快捷方式将不起作用.

    ; 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)
  4. 如果您需要进行大量搜索,请为其编写一个函数:

    (defn seq-contains? [coll target] (some #(= target %) coll))
    (seq-contains? [true false true] false) ; = true
    
    Run Code Online (Sandbox Code Playgroud)

另外,请参阅Michał的答案,了解如何检查序列中是否包含多个目标.


G__*_*G__ 5

这是我用于此目的的标准实用程序中的快速功能:

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


Sim*_*oke 5

这是经典的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)

  • 好吧,Clojure中一个糟糕的解决方案的原因是它在一个处理器上递归堆栈.一个更好的Clojure解决方案是<pre>(defn member?[elt col](一些#(= elt%)col))</ pre>这是因为`some`可能在可用内核之间并行. (4认同)