2013-04-28 19 views
10

Czy istnieje idiomatyczny sposób określania, czy LazySeq zawiera element? Począwszy od Clojure 1.5 nazywając contains? rzuca IllegalArgumentException:Clojure: w jaki sposób wywoływać idiomatyczne połączenia? na leniwej sekwencji

IllegalArgumentException contains? not supported on type: clojure.lang.LazySeq  
clojure.lang.RT.contains (RT.java:724) 

Przed 1.5, o ile mi wiadomo, to zawsze zwrócony fałszywe.

Wiem, że wywołanie contains? na LazySeq może nigdy nie wrócić, ponieważ może być nieskończone. Ale co, jeśli wiem, że tak nie jest i nie obchodzi mnie, czy jest on oceniany z niecierpliwością?

Co wymyśliłem to:

(defn lazy-contains? [col key] 
    (not (empty? (filter #(= key %) col)))) 

Ale nie czuje się całkiem dobrze. Czy istnieje lepszy sposób?

Odpowiedz

11

Po pierwsze, leniwe seqy nie są skuteczne w sprawdzaniu członkostwa. Rozważ użycie zestawu zamiast leniwego seq.

Jeśli zestaw jest niepraktyczny, Twoje rozwiązanie nie jest złe. Kilka możliwych ulepszeń:

  1. "Nie pusty" jest nieco niezręczny. Samo użycie seq wystarczy, by uzyskać wartość zerową lub prawdę, której mogą używać twoi użytkownicy. Jeśli chcesz true lub false, możesz zawijać ją w wartość boolowską.

  2. Ponieważ zależy ci tylko na pierwszym dopasowaniu, możesz użyć niektórych zamiast filtrów i seq.

  3. Wygodnym sposobem napisania predykatu równości jest zestaw literowy, np. # {Klucz}, ale jeśli klucz jest zerowy, to zawsze zwróci zero, niezależnie od tego, czy nie znaleziono naszego nie.

Wszystko razem to daje:

(defn lazy-contains? [col key] 
    (some #{key} col)) 
+0

Przypadek posiadania zer jako klucza powoduje, że jest on niepoprawny. Ale ponieważ w moim przypadku klucz nigdy nie jest zerowy, mogę z tym żyć. – nansen

+0

Dobrze. Aby to naprawić, po prostu użyj oryginalnego predykatu: (niektóre # (= klucz%) col) – Chouser

+0

@Chouser Próbowałem wdrożyć dowolne z tych alternatyw dla konkretnego zastosowania i wystąpił następujący problem. Jak mogę to lepiej dopasować do oryginału? funkcjonalność? (lazy-zawiera? {: Stan "aktywny",: course_n "prawo",: course_i "C0"}: stan) zwraca zero , ale gdy korzystanie zawiera? to zwraca true Próbuję również przeglądać clojuredocs i nie widzę jak to naprawić. – RatavaWen

4

Jeśli użyjesz some zamiast filter, jak w twoim przykładzie, otrzymasz natychmiastowy zwrot, gdy tylko zostanie znaleziona wartość, zamiast wymusić ocenę całej sekwencji.

(defn lazy-contains? [coll key] 
    (boolean (some #(= % key) coll))) 

Edit: Jeśli nie zmuszania wynik do wartości logicznej, należy pamiętać, że dostaniesz nil zamiast false jeśli klucz nie zostanie znaleziony.

+0

Korzystanie filtr jak ja nie ocenia całą sekwencję tak długo jak wartość znajduje się w pierwszej kolejności. '(lazy-zawiera? (zakres) 100)' zwraca true. Czy to nie jest odpowiednik twojej funkcji? – nansen

Powiązane problemy