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.