Clojure에 목록에 특정 값이 포함되어 있는지 테스트
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
모든 유형의 컬렉션 으로 전달 될 수 있습니다 .)
동일한 목적을위한 표준 유틸리티는 다음과 같습니다.
(defn in?
"true if coll contains elm"
[coll elm]
(some #(= elm %) coll))
조금 늦었다는 것을 알고 있습니다.
(contains? (set '(101 102 103)) 102)
마지막으로 clojure 1.4에서 true 출력 :)
.methodName 구문을 사용하여 Java 메소드를 항상 호출 할 수 있습니다.
(.contains [100 101 102] 101) => true
(not= -1 (.indexOf '(101 102 103) 102))
작동하지만 아래가 더 좋습니다.
(some #(= 102 %) '(101 102 103))
가치가있는 것은 목록에 대한 contains 함수를 간단하게 구현 한 것입니다.
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (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ł의 답변 을 참조하십시오 .
이 목적으로 사용하는 표준 유틸리티 중 빠른 기능이 있습니다.
(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))))
다음은 클래식 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)))
jg-faustus 버전 의 "list-contains?"를 기반으로 작성했습니다. 이제는 여러 가지 인수가 필요합니다.
(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))))
It is as simple as using a set - similar to maps, you can just drop it in the function position. It evaluates to the value if in the set (which is truthy) or nil
(which is falsey):
(#{100 101 102} 101) ; 101
(#{100 101 102} 99) ; nil
If you're checking against a reasonably sized vector/list you won't have until runtime, you can also use the set
function:
; (def nums '(100 101 102))
((set nums) 101) ; 101
The recommended way is to use some
with a set - see documentation for clojure.core/some
.
You could then use some
within a real true/false predicate, e.g.
(defn in? [coll x] (if (some #{x} coll) true false))
(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))))))
(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 )))
example usage (which? [ 1 2 3 ] 3) or (which? #{ 1 2 3} 4 5 3)
The problem with the 'recommended' solution is it is breaks when the value you are seeking is 'nil'. I prefer this solution:
(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))
There are convenient functions for this purpose in the Tupelo library. In particular, the functions contains-elem?
, contains-key?
, and contains-val?
are very useful. Full documentation is present in the API docs.
contains-elem?
is the most generic and is intended for vectors or any other clojure 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))))
Here we see that for an integer range or a mixed vector, contains-elem?
works as expected for both existing and non-existant elements in the collection. For maps, we can also search for any key-value pair (expressed as a len-2 vector):
(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] ))))
It is also straightforward to search a set:
(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)))))
For maps & sets, it is simpler (& more efficient) to use contains-key?
to find a map entry or a set element:
(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)))
And, for maps, you can also search for values with 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)))
As seen in the test, each of these functions works correctly when for searching for nil
values.
Since Clojure is built on Java, you can just as easily call the .indexOf
Java function. This function returns the index of any element in a collection, and if it can't find this element, returns -1.
Making use of this we could simply say:
(not= (.indexOf [1 2 3 4] 3) -1)
=> true
참고URL : https://stackoverflow.com/questions/3249334/test-whether-a-list-contains-a-specific-value-in-clojure
'Programing' 카테고리의 다른 글
Guzzle 6 : 응답을위한 더 이상 json () 메소드 (0) | 2020.06.10 |
---|---|
프로그래밍 방식으로 축소 또는 축소 축소 도구 모음 레이아웃 (0) | 2020.06.10 |
팬더 데이터 프레임의 열을 반복하여 회귀를 실행하는 방법 (0) | 2020.06.09 |
왜 파이썬 3에서 부동 소수점 값 4 * 0.1이 멋지게 보이지만 3 * 0.1은 그렇지 않습니까? (0) | 2020.06.09 |
Spring에서 Tomcat이 제공하는 JNDI DataSource를 사용하는 방법은 무엇입니까? (0) | 2020.06.09 |