Jaki jest najlepszy sposób sprawdzenia, czy lista zawiera daną wartość w Clojure?
W szczególności contains?
wprawia mnie w zakłopotanie zachowanie :
(contains? '(100 101 102) 101) => false
Mógłbym oczywiście napisać prostą funkcję, aby przejść przez listę i przetestować pod kątem równości, ale z pewnością musi istnieć standardowy sposób zrobienia tego?
data-structures
clojure
mikera
źródło
źródło
Odpowiedzi:
Ach,
contains?
podobno jedno z pięciu najczęściej zadawanych pytań dotyczących Clojure.To ma nie sprawdzić, czy kolekcja zawiera wartość; sprawdza, czy element można pobrać za pomocą
get
lub, innymi słowy, czy kolekcja zawiera klucz. Ma to sens w przypadku zbiorów (które można uważać za nie rozróżniające kluczy i wartości), map (tak samo(contains? {:foo 1} :foo)
jesttrue
) i wektorów (ale zauważ, że(contains? [:foo :bar] 0)
to jesttrue
, ponieważ klucze są tutaj indeksami, a dany wektor „zawiera” indeks0
!).Aby dodać do zamieszania, w przypadkach, gdy dzwonienie nie ma sensuAktualizacja: W Clojure ≥ 1,5contains?
, po prostu wracafalse
; to się dzieje w(contains? :foo 1)
i także(contains? '(100 101 102) 101)
.contains?
wyrzuca, gdy wręczany jest obiekt typu, który nie obsługuje zamierzonego testu „kluczowego członkostwa”.Prawidłowy sposób na zrobienie tego, co próbujesz zrobić, jest następujący:
Szukając jednego z kilku przedmiotów, możesz użyć większego zestawu; szukając
false
/nil
, możesz użyćfalse?
/nil?
- ponieważ(#{x} x)
zwracax
, więc(#{nil} nil)
jestnil
; podczas wyszukiwania jednej z wielu pozycji, z których niektóre mogą byćfalse
lubnil
których możesz użyć(Zwróć uwagę, że elementy można przekazywać do
zipmap
dowolnego typu kolekcji).źródło
(some #{101} '(100 101 102))
, że powiedział, że „przez większość czasu to działa”. Czy nie można powiedzieć, że to zawsze działa? Używam Clojure 1.4 i dokumentacja używa tego rodzaju przykładu. Działa na mnie i ma sens. Czy jest jakiś szczególny przypadek, w którym to nie działa?false
lubnil
- zobacz następny akapit. Odrębną uwagę, w Clojure 1.5-RC1contains?
zgłasza wyjątek, gdy jako argument podano kolekcję bez klucza. Przypuszczam, że poprawię tę odpowiedź, gdy pojawi się ostateczna wersja.Oto moje standardowe narzędzie do tego samego celu:
źródło
nil
ifalse
. Dlaczego to nie jest częścią clojure / core?seq
można zmienić nazwę nacoll
, aby uniknąć pomyłki z funkcjąseq
?seq
wewnątrz ciała, nie ma konfliktu z parametrem o tej samej nazwie. Możesz jednak edytować odpowiedź, jeśli uważasz, że zmiana nazwy ułatwiłaby jej zrozumienie.(boolean (some #{elm} coll))
przypadku, gdy nie musisz się martwićnil
lubfalse
.Zawsze możesz wywołać metody Java za pomocą składni .methodName.
źródło
contains?
, Qc Na uderzył go Bô i powiedział: „Głupi uczniu! Musisz zdać sobie sprawę, że nie ma łyżki. Pod spodem jest tylko Java! Użyj notacji kropkowej.”. W tym momencie Anton osiągnął oświecenie.Wiem, że jestem trochę spóźniony, ale co z:
W końcu w clojure 1.4 wyświetla prawdę :)
źródło
(set '(101 102 103))
jest taki sam jak%{101 102 103}
. Twoja odpowiedź może być zapisana jako(contains? #{101 102 103} 102)
.'(101 102 103)
do zestawu.Działa, ale poniżej jest lepiej:
źródło
Warto, oto moja prosta implementacja funkcji zawiera dla list:
źródło
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
Jeśli masz wektor lub listę i chcesz sprawdzić, czy dana wartość jest zawarta w nim, okaże się, że
contains?
nie działa. Michał już wyjaśnił, dlaczego .W tym przypadku możesz wypróbować cztery rzeczy:
Zastanów się, czy naprawdę potrzebujesz wektora czy listy. Jeśli zamiast tego użyjesz zestawu ,
contains?
zadziała.Użyj
some
, owijając cel w zestaw w następujący sposób:Skrót ustaw jako funkcję nie będzie działał, jeśli szukasz fałszywej wartości (
false
lubnil
).W takich przypadkach należy użyć wbudowanej funkcji predykatu dla tej wartości
false?
lubnil?
:Jeśli będziesz musiał często przeprowadzać tego rodzaju wyszukiwanie, napisz dla niego funkcję :
Zobacz także odpowiedź Michała, aby dowiedzieć się, jak sprawdzić, czy któryś z wielu celów znajduje się w sekwencji.
źródło
Oto szybka funkcja z moich standardowych narzędzi, których używam do tego celu:
źródło
Oto klasyczne rozwiązanie Lisp:
źródło
some
jest to potencjalnie równoległe dla dostępnych rdzeni.Zbudowałem na podstawie jg-faustus wersji „list-zawiera?”. Teraz wymaga dowolnej liczby argumentów.
źródło
Jest to tak proste, jak użycie zestawu - podobnie jak mapy, możesz po prostu upuścić go w pozycji funkcji. Zwraca wartość if w zestawie (co jest prawdą) lub
nil
(co jest błędne):Jeśli porównujesz wektor / listę o rozsądnych rozmiarach, których nie będziesz mieć przed uruchomieniem, możesz również użyć
set
funkcji:źródło
Zalecanym sposobem jest użycie
some
z zestawem - patrz dokumentacjaclojure.core/some
.Możesz wtedy użyć
some
rzeczywistego predykatu prawda / fałsz, npźródło
if
true
ifalse
?some
już zwraca wartości prawda-ish i false-ish.źródło
przykładowe użycie (które? [1 2 3] 3) lub (które? # {1 2 3} 4 5 3)
źródło
Ponieważ Clojure jest zbudowany na Javie, równie łatwo możesz wywołać funkcję
.indexOf
Java. Ta funkcja zwraca indeks dowolnego elementu w kolekcji, a jeśli nie może znaleźć tego elementu, zwraca -1.Korzystając z tego moglibyśmy po prostu powiedzieć:
źródło
Problem z „zalecanym” rozwiązaniem polega na tym, że łamie się, gdy szukana wartość wynosi „zero”. Wolę takie rozwiązanie:
źródło
W bibliotece Tupelo dostępne są wygodne funkcje służące do tego celu . W szczególności, funkcje
contains-elem?
,contains-key?
icontains-val?
są bardzo przydatne. Pełna dokumentacja znajduje się w dokumentacji API .contains-elem?
jest najbardziej ogólny i jest przeznaczony dla wektorów lub innych klastrówseq
:Tutaj widzimy, że dla zakresu liczb całkowitych lub wektora mieszanego
contains-elem?
działa zgodnie z oczekiwaniami zarówno dla istniejących, jak i nieistniejących elementów w kolekcji. W przypadku map możemy również wyszukać dowolną parę klucz-wartość (wyrażoną jako wektor len-2):Przeszukiwanie zbioru jest również proste:
W przypadku map i zestawów prostsze (i wydajniejsze) jest użycie
contains-key?
do znalezienia wpisu mapy lub elementu zestawu:W przypadku map możesz również wyszukiwać wartości za pomocą
contains-val?
:Jak widać w teście, każda z tych funkcji działa poprawnie podczas wyszukiwania
nil
wartości.źródło
Inna opcja:
Użyj java.util.Collection # zawiera ():
źródło