Come convertire una sequenza pigra in una sequenza non pigra in Clojure


95

Ho provato quanto segue in Clojure, aspettandomi di ricevere la classe di una sequenza non pigra:

(.getClass (doall (take 3 (repeatedly rand))))

Tuttavia, questo ritorna ancora clojure.lang.LazySeq. La mia ipotesi è che doallvaluti l'intera sequenza, ma restituisca la sequenza originale poiché è ancora utile per la memorizzazione.

Allora qual è il mezzo idiomatico per creare una sequenza non pigra da una pigra?


Sono sorpreso che nessuno ti abbia chiesto perché sei preoccupato per il tipo effettivo del valore di ritorno didoall
tar

Potresti convertire in un vettore:(vec (take 3 (repeatedly rand)))
Kris

Risposte:


161

doallé tutto quello di cui hai bisogno. Solo perché il seqtipo has LazySeqnon significa che sia in attesa di valutazione. I pigri seqmemorizzano nella cache i loro risultati, quindi tutto ciò che devi fare è camminare il pigro sequna volta (come doallfa) per forzare tutto e quindi renderlo non pigro. seqnon non forzare l'intera collezione da valutare.


2
L'ho modificato nella risposta accettata. In una nota correlata, con quali mezzi è possibile determinare se un LazySeq è stato precedentemente valutato?
Tim Clemons,

10
Credo che tu chiami realized?.
toofarsideways

1
Probabilmente dovrebbe esserci realizeun'operazione da abbinare realized?.
Reut Sharabani

È tutto molto carino. Ma poiché alcune funzioni come contains?non si preoccupano se hai realizzato o meno il seq pigro, questo risponde alla domanda specifica come posta, ma meno al titolo della domanda.
matante

75

Questa è in una certa misura una questione di tassonomia. una sequenza pigra è solo un tipo di sequenza come una lista, un vettore o una mappa. Quindi la risposta è ovviamente "dipende dal tipo di sequenza non pigra che desideri ottenere:
scegli tra:

  • una sequenza pigra ex-pigra (completamente valutata) (doall ... )
  • un elenco per l'accesso sequenziale (apply list (my-lazy-seq)) OR (into () ...)
  • un vettore per un successivo accesso casuale (vec (my-lazy-seq))
  • una mappa o un set se hai uno scopo speciale.

Puoi avere qualsiasi tipo di sequenza che soddisfi le tue esigenze.


Questa è la migliore risposta.
Felipe

4
La risposta accettata è tecnicamente corretta, ma questa risposta mi è stata molto utile. Stavo cercando di mappare una funzione su un vettore e poi sputare i risultati in un file, e anche dopo aver chiamato doall, il file conteneva "clojure.lang.LazySeq@address" invece del contenuto della sequenza. Chiamare vec sulla value map restituita mi ha fatto ottenere ciò di cui avevo bisogno per sputare nel file.
Jesse Rosalia

1
@JesseRosalia È bello sapere che l'unica risposta di Rich Hickey in tutto SO era tecnicamente corretta. ;-)
Phil Cooper

(vec (my-lazy-seq))non è così bello in situazioni come le seguenti: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Dal momento che cheshiresceglie di produrre un lazy-seq da(json/parse-string)
codeasone

Mitigazione quanto già in precedenza era di usare ansioso(json/parse-string-strict)
codeasone

22

Questo ragazzo ricco sembra conoscere il suo clojure ed ha assolutamente ragione.
Buth, penso che questo frammento di codice, usando il tuo esempio, potrebbe essere un utile complemento a questa domanda:

=> (realized? (take 3 (repeatedly rand))) 
false
=> (realized? (doall (take 3 (repeatedly rand)))) 
true

In effetti il tipo non è cambiato ma la realizzazione è cambiata


2
Vale la pena notare, tuttavia, che non è necessario forzare l'intera sequenza per realized?tornare true. Ad esempio(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry

22
Questo ragazzo ricco: D haha
nimrod

10
@nimrod :) il gioco di parole però doveva essere in "hís clojure".
Peter

10
Per chi non lo sapesse, "il ragazzo ricco" ha inventato Clojure.
erturne

1
@AlexCoventry il tuo esempio restituisce[true true]

7

Mi sono imbattuto in questo post del blog sul doallnon essere ricorsivo. Per questo ho scoperto che il primo commento nel post ha funzionato. Qualcosa sulla falsariga di:

(use 'closure.walk)
(postwalk identity nested-lazy-thing)

L'ho trovato utile in uno unit test in cui volevo forzare la valutazione di alcune applicazioni annidate mapper forzare una condizione di errore.


5
(.getClass (into '() (take 3 (repeatedly rand))))

3
Questa è un'idea terribile. Inverte la sequenza di input.
amalloy

3
Certo, in questo caso invertire l'input non fa differenza, dato che sono solo 3 numeri casuali .... :-)
mikera
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.