Clojure에 주어진 값이 목록에 포함되어 있는지 테스트하는 가장 좋은 방법은 무엇입니까?
특히의 행동 contains?
은 현재 나를 혼란스럽게합니다.
(contains? '(100 101 102) 101) => false
분명히 목록을 탐색하고 평등을 테스트하는 간단한 함수를 작성할 수는 있지만 반드시 표준 방법이 있어야합니까?
Clojure에 주어진 값이 목록에 포함되어 있는지 테스트하는 가장 좋은 방법은 무엇입니까?
특히의 행동 contains?
은 현재 나를 혼란스럽게합니다.
(contains? '(100 101 102) 101) => false
분명히 목록을 탐색하고 평등을 테스트하는 간단한 함수를 작성할 수는 있지만 반드시 표준 방법이 있어야합니까?
답변:
아, contains?
... 아마도 5 가지 자주 묻는 질문 중 하나 인 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
모든 유형의 컬렉션 으로 전달 될 수 있습니다 .)
(some #{101} '(100 101 102))
"대부분 이것이 작동한다"고 말하는 것에 대해 언급했습니다. 항상 작동한다고 말하는 것이 공평하지 않습니까? Clojure 1.4를 사용하고 있으며 문서는 이러한 종류의 예제를 사용합니다. 그것은 나를 위해 작동하고 의미가 있습니다. 작동하지 않는 특별한 경우가 있습니까?
false
또는 존재 여부를 확인하는 경우 작동하지 않습니다 nil
. 다음 단락을 참조하십시오. 별도의 메모에서 Clojure 1.5-RC1 contains?
에서는 키가 아닌 컬렉션이 인수로 주어지면 예외가 발생합니다. 최종 릴리스가 나올 때이 답변을 편집한다고 가정합니다.
동일한 목적을위한 표준 유틸리티는 다음과 같습니다.
(defn in?
"true if coll contains elm"
[coll elm]
(some #(= elm %) coll))
nil
과 false
. 왜 이것이 클로저 / 코어의 일부가 아닌가?
seq
coll
함수와의 혼동을 피하기 위해 로 이름을 바꿀 수 seq
있습니까?
seq
. 본문 안에서 함수를 사용하지 않기 때문에 같은 이름의 매개 변수와 충돌하지 않습니다. 그러나 이름을 바꾸면 이해하기 쉽다고 생각되면 답을 자유롭게 편집하십시오.
(boolean (some #{elm} coll))
걱정할 필요가없는 경우 보다 3-4 배 느려질 수 있습니다 . nil
false
.methodName 구문을 사용하여 Java 메소드를 항상 호출 할 수 있습니다.
(.contains [100 101 102] 101) => true
contains?
(QC) 나은 보에 그를 명중하고 말했다 : "!. 바보 학생이 더 숟가락이없는 실현해야 ! 그것은 아래 모든 단지 자바의 점 표기법을 사용.". 그 순간 Anton은 깨달았습니다.
가치가있는 것은 목록에 대한 포함 함수를 간단하게 구현 한 것입니다.
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (first s) value) true (recur (rest s) value))
false)))
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
벡터 또는 목록이 있고 값 이 포함되어 있는지 확인하려는 contains?
경우 작동하지 않습니다. Michał은 이미 그 이유를 설명했습니다 .
; does not work as you might expect
(contains? [:a :b :c] :b) ; = false
이 경우 시도 할 수있는 네 가지가 있습니다.
실제로 벡터 또는 목록이 필요한지 고려하십시오. 당신이 경우 대신 세트를 사용 , contains?
작동합니다.
(contains? #{:a :b :c} :b) ; = true
다음some
과 같이을 사용 하여 대상을 세트로 묶습니다.
(some #{:b} [:a :b :c]) ; = :b, which is truthy
잘못된 값 ( false
또는 nil
)을 검색하는 경우 기능으로 설정 바로 가기가 작동하지 않습니다 .
; will not work
(some #{false} [true false true]) ; = nil
이러한 경우에, 당신이해야 사용 내장 술어 기능을 그 값에 대한, false?
또는 nil?
:
(some false? [true false true]) ; = true
이런 종류의 검색을 많이 해야하는 경우 함수를 작성하십시오 .
(defn seq-contains? [coll target] (some #(= target %) coll))
(seq-contains? [true false true] false) ; = true
또한 여러 대상이 시퀀스에 포함되어 있는지 확인하는 방법 은 Michał의 답변 을 참조하십시오 .
다음은 클래식 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)))
some
사용 가능한 코어에서 잠재적으로 병렬 이기 때문 입니다.
"list-contains?"의 jg-faustus 버전 을 기반으로 작성했습니다 . 이제는 여러 가지 인수가 필요합니다.
(defn list-contains?
([collection value]
(let [sequence (seq collection)]
(if sequence (some #(= value %) sequence))))
([collection value & next]
(if (list-contains? collection value) (apply list-contains? collection next))))
(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)
Tupelo 라이브러리에는 이 목적 을 위한 편리한 기능이 있습니다 . 특히, 기능 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
값 을 검색 할 때 올바르게 작동합니다 .