2013-04-28 10 views
10

Esiste un modo idiomatico per determinare se un LazySeq contiene un elemento? A partire dal Clojure 1,5 chiamare contains? lancia un IllegalArgumentException:Clojure: il modo idiomatico da chiamare contiene? su una sequenza lenta

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

Prima 1.5, per quanto ne so, è sempre restituito false.

So che chiamare su un LazySeq potrebbe non tornare mai in quanto può essere infinito. Ma cosa succede se so che non è e non mi interessa se viene valutato con entusiasmo?

Quello che mi è venuto in mente è:

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

Ma non si sente abbastanza di destra. C'è un modo migliore?

risposta

11

In primo luogo, le lazy seq non sono efficienti per il controllo dell'appartenenza. Prendi in considerazione l'utilizzo di un set anziché di un pigro seq.

Se un set non è pratico, la soluzione non è male. Un paio di possibili miglioramenti:

  1. "Non vuoto" è un po 'imbarazzante. Usare solo seq è sufficiente per ottenere un valore nullo o veritiero che gli utenti possono usare in un if. Puoi avvolgerlo in booleano se vuoi vero o falso.

  2. Dato che ti interessa solo la prima partita, puoi usare un po 'invece di filtro e seq.

  3. Un modo conveniente per scrivere un predicato di uguaglianza è con un set letterale, come # {chiave}, sebbene se la chiave è nil questo restituirà sempre nil se nil si trova il nostro no.

Tutti insieme che ti dà:

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

Il caso di avere nil come chiave lo rende un po 'scorretto. Ma dal momento che nel mio caso la chiave non è mai nulla, posso conviverci. – nansen

+0

Giusto. Per risolvere il problema, usa semplicemente il tuo predicato originale: (alcuni # (= chiave%) col) – Chouser

+0

@Chouser Ho cercato di implementare una di queste alternative a un uso specifico e ho riscontrato il seguente problema. Come otterrei questo per abbinare più strettamente l'originale contiene? funzionalità? (lazy-contiene? {: Stato "attivo",: corso_n "legge",: corso_i "C0"}: stato) restituisce nil ma quando si utilizza contiene? restituisce true Sto provando a digerire attraverso clojuredocs pure e non vedo come risolverlo. – RatavaWen

4

Se si utilizza some anziché filter come nell'esempio, si otterrà un ritorno immediato non appena viene trovato un valore anziché forzare la valutazione dell'intera sequenza.

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

Edit: Se non costringere il risultato ad un valore booleano, notare che si otterrà nil invece di false se la chiave non è stata trovata.

+0

inserimento del filtro come ho fatto io non valuta l'intera sequenza fino a quando il valore viene trovato per primo. '(lazy-contains? (intervallo) 100)' restituisce true. Quindi non è equivalente alla tua funzione? – nansen

Problemi correlati