Что состоит в том, чтобы протестировать лучший способ, содержит ли список данное значение в Clojure?
В частности, поведение contains?
в настоящее время смущает меня:
(contains? '(100 101 102) 101) => false
Я мог, очевидно, записать простую функцию, чтобы пересечь список и протестировать на равенство, но должен, конечно, быть стандартный способ сделать это?
А, 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))
При поиске одного из множества элементов можно использовать большее множество; при поиске false
/ nil
можно использовать false?
/ nil?
-- потому что (#{x} x)
возвращает x
, поэтому (#{nil} nil)
является nil
; при поиске одного из нескольких элементов, некоторые из которых могут быть false
или nil
, вы можете использовать
(some (zipmap [...the items...] (repeat true)) the-collection)
(Обратите внимание, что элементы могут быть переданы в zipmap
в любом типе коллекции. )
Если уж на то пошло, вот моя простая реализация функции contains для списков:
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (first s) value) true (recur (rest s) value))
false)))
Вот быстрая функция из моих стандартных утилит, которые я использую для этой цели:
(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 in?
"true if coll contains elm"
[coll elm]
(some #(= elm %) coll))