Каков наилучший способ проверить, содержит ли список заданное значение в Clojure?
В частности, поведение contains?
меня сейчас смущает:
(contains? '(100 101 102) 101) => false
Я мог бы, очевидно, написать простую функцию для обхода списка и проверки на равенство, но наверняка должен быть стандартный способ сделать это?
data-structures
clojure
mikera
источник
источник
Ответы:
Ах,
contains?
якобы один из пяти часто задаваемых вопросов о: Clojure.Он не проверяет, содержит ли коллекция значение; он проверяет, можно ли получить элемент с помощью
get
или, другими словами, содержит ли коллекция ключ. Это имеет смысл для множеств (которые можно рассматривать как не делающие различий между ключами и значениями), картами (как(contains? {:foo 1} :foo)
естьtrue
) и векторами (но обратите внимание, что(contains? [:foo :bar] 0)
этоtrue
потому, что ключи здесь являются индексами, а рассматриваемый вектор «содержит» индекс0
!)Чтобы добавить путаницу, в случаях, когда нет смысла звонитьОбновление: в Clojure ≥ 1,5contains?
, он просто возвращаетсяfalse
; это то, что происходит,(contains? :foo 1)
а также(contains? '(100 101 102) 101)
.contains?
выдает при передаче объект типа, который не поддерживает предполагаемый тест «членство в ключе».Правильный способ сделать то, что вы пытаетесь сделать, заключается в следующем:
При поиске одного из множества предметов вы можете использовать больший набор; при поиске
false
/nil
, вы можете использоватьfalse?
/nil?
- потому что(#{x} x)
возвращаетx
, таким образом,(#{nil} nil)
естьnil
; при поиске одного из нескольких элементов, некоторые из которых могут бытьfalse
илиnil
, вы можете использовать(Обратите внимание, что элементы могут быть переданы
zipmap
в любой тип коллекции.)источник
(some #{101} '(100 101 102))
высказывание, что «большую часть времени это работает». Разве не справедливо сказать, что это всегда работает? Я использую Clojure 1.4, и в документации используется такой пример. Это работает для меня и имеет смысл. Есть ли какой-то особый случай, когда он не работает?false
илиnil
- см. Следующий абзац. Отдельно отметим, что в Clojure 1.5-RC1contains?
выдает исключение, когда в качестве аргумента приводится неключевая коллекция. Полагаю, я отредактирую этот ответ, когда выйдет финальная версия.Вот мой стандартный утилит для той же цели:
источник
nil
иfalse
. Теперь, почему это не является частью clojure / core?seq
Может быть, можно переименоватьcoll
, чтобы избежать путаницы с функциейseq
?seq
внутри тела, нет конфликта с параметром с тем же именем. Но не стесняйтесь редактировать ответ, если вы думаете, что переименование облегчит его понимание.(boolean (some #{elm} coll))
если вам не о чем беспокоитьсяnil
илиfalse
.Вы всегда можете вызывать Java-методы с синтаксисом .methodName.
источник
contains?
, Qc Na ударил его Бо и сказал: «Глупый ученик! Ты должен понять, что ложки нет. Это всего лишь Java внизу! Используйте точечную запись». В этот момент Антон стал просветленным.Я знаю, что я немного опоздал, но как насчет:
Наконец-то в clojure 1.4 выводы верные :)
источник
(set '(101 102 103))
так же, как%{101 102 103}
. Таким образом, ваш ответ может быть записан как(contains? #{101 102 103} 102)
.'(101 102 103)
в набор.Работает, но ниже лучше:
источник
Для чего это стоит, это моя простая реализация функции содержит списки:
источник
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
Если у вас есть вектор или список и вы хотите проверить, содержится ли в нем значение , вы обнаружите, что
contains?
оно не работает. Михал уже объяснил почему .В этом случае вы можете попробовать четыре вещи:
Подумайте, действительно ли вам нужен вектор или список. Если вы используете набор вместо ,
contains?
будет работать.Используйте
some
, оборачивая цель в наборе, следующим образом:Ярлык set-as-function не будет работать, если вы ищете ложное значение (
false
илиnil
).В этих случаях вам следует использовать встроенную функцию предиката для этого значения
false?
илиnil?
:Если вам нужно много искать, напишите для него функцию :
Также см . Ответ Михала о том, как проверить, содержится ли какая-либо из нескольких целей в последовательности.
источник
Вот краткая функция из моих стандартных утилит, которые я использую для этой цели:
источник
Вот классическое решение Lisp:
источник
some
он потенциально параллелен между доступными ядрами.Я основывался на версии jg-faustus «list-contains?». Теперь требуется любое количество аргументов.
источник
Это так же просто, как использовать набор - аналогично картам, вы можете просто поместить его в положение функции. Это оценивает к значению, если в наборе (который является правдивым) или
nil
(который является ложным):Если вы проверяете вектор / список разумного размера, которого у вас не будет до времени выполнения, вы также можете использовать
set
функцию:источник
Рекомендуемый способ - использовать
some
с набором - см. Документацию дляclojure.core/some
.Вы можете использовать
some
реальный предикат true / false, напримеристочник
if
true
иfalse
?some
уже возвращает значения true-ish и false-ish.источник
пример использования (который? [1 2 3] 3) или (который? # {1 2 3} 4 5 3)
источник
Поскольку Clojure построен на Java, вы также можете легко вызвать
.indexOf
функцию Java. Эта функция возвращает индекс любого элемента в коллекции, и если она не может найти этот элемент, возвращает -1.Используя это, мы могли бы просто сказать:
источник
Проблема с «рекомендуемым» решением состоит в том, что оно разрывается, когда значение, которое вы ищете, - «ноль». Я предпочитаю это решение:
источник
Для этого есть удобные функции в библиотеке Тупело . В частности, функция
contains-elem?
,contains-key?
иcontains-val?
очень полезна. Полная документация представлена в документации по API .contains-elem?
является наиболее общим и предназначен для векторов или любой другой ситуацииseq
:Здесь мы видим, что для целочисленного диапазона или смешанного вектора,
contains-elem?
работает как ожидалось для существующих и несуществующих элементов в коллекции. Для карт мы также можем искать любую пару ключ-значение (выраженную в виде вектора len-2):Также легко найти набор:
Для карт и наборов проще (и более эффективно) использовать
contains-key?
для поиска записи карты или элемента набора:А для карт вы также можете искать значения с помощью
contains-val?
:Как видно из теста, каждая из этих функций работает правильно при поиске
nil
значений.источник
Другой вариант:
Используйте java.util.Collection # Содержит ():
источник