Проверьте, содержит ли список конкретное значение в Clojure - PullRequest
150 голосов
/ 14 июля 2010

Как лучше всего проверить, содержит ли список заданное значение в Clojure?

В частности, поведение contains? в настоящее время сбивает меня с толку:

(contains? '(100 101 102) 101) => false

Я, очевидно, мог бы написать простую функцию для обхода списка и проверки на равенство, но наверняка должен быть стандартный способ сделать это?

Ответы [ 17 ]

2 голосов
/ 28 апреля 2015

Это так же просто, как использовать набор - аналогично картам, вы можете просто опустить его в положение функции.Он оценивается как значение в наборе (который является правдивым) или nil (который является ошибочным):

(#{100 101 102} 101) ; 101
(#{100 101 102} 99) ; nil

Если вы проверяете вектор / список разумного размера, у вас не будетдо времени выполнения вы также можете использовать функцию set:

; (def nums '(100 101 102))
((set nums) 101) ; 101
1 голос
/ 16 марта 2013

Рекомендуется использовать some с набором - см. Документацию для clojure.core/some.

Затем можно использовать some в реальном предикате true / false, например,

* 1007.*
1 голос
/ 16 июля 2013
(defn in?
  [needle coll]
  (when (seq coll)
    (or (= needle (first coll))
        (recur needle (next coll)))))

(defn first-index
  [needle coll]
  (loop [index 0
         needle needle
         coll coll]
    (when (seq coll)
      (if (= needle (first coll))
        index
        (recur (inc index) needle (next coll))))))
1 голос
/ 19 июня 2016
(defn which?
 "Checks if any of elements is included in coll and says which one
  was found as first. Coll can be map, list, vector and set"
 [ coll & rest ]
 (let [ncoll (if (map? coll) (keys coll) coll)]
    (reduce
     #(or %1  (first (filter (fn[a] (= a %2))
                           ncoll))) nil rest )))

пример использования (какой? [1 2 3] 3) или (какой? # {1 2 3} 4 5 3)

0 голосов
/ 18 февраля 2017

Для этого есть удобные функции в библиотеке Тупело .В частности, функции contains-elem?, contains-key? и contains-val? очень полезны.Полная документация присутствует в документации API .

contains-elem? является наиболее общей и предназначена для векторов или любой другой ситуации seq:

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

Здесь мы видим, что для целочисленного диапазона или смешанного вектора contains-elem? работает как ожидалось как для существующих, так и для несуществующих элементов в коллекции.Для карт мы также можем искать любую пару ключ-значение (выраженную в виде вектора len-2):

 (testing "maps"
    (let [coll {1 :two "three" \4}]
      (isnt (contains-elem? coll nil ))
      (isnt (contains-elem? coll [1 :no-way] ))
      (is   (contains-elem? coll [1 :two]))
      (is   (contains-elem? coll ["three" \4])))
    (let [coll {1 nil "three" \4}]
      (isnt (contains-elem? coll [nil 1] ))
      (is   (contains-elem? coll [1 nil] )))
    (let [coll {nil 2 "three" \4}]
      (isnt (contains-elem? coll [1 nil] ))
      (is   (contains-elem? coll [nil 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)))

И для карт вы также можете искать значения с помощью contains-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.

0 голосов
/ 29 апреля 2016

Проблема с «рекомендуемым» решением заключается в том, что оно разрывается, когда искомое значение равно «ноль». Я предпочитаю это решение:

(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))
0 голосов
/ 27 января 2016

Поскольку Clojure построен на Java, вы также можете легко вызвать функцию .indexOf Java. Эта функция возвращает индекс любого элемента в коллекции, и если она не может найти этот элемент, возвращает -1.

Используя это, мы могли бы просто сказать:

(not= (.indexOf [1 2 3 4] 3) -1)
=> true
...