Como converter uma sequência preguiçosa em não preguiçosa em Clojure

95

Tentei o seguinte em Clojure, esperando o retorno da classe de uma sequência não preguiçosa:

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

No entanto, isso ainda retorna clojure.lang.LazySeq. Meu palpite é que doallavalia a sequência inteira, mas retorna a sequência original, pois ainda é útil para memoização.

Então, qual é o meio idiomático de criar uma sequência não preguiçosa a partir de uma sequência preguiçosa?

Tim Clemons
fonte
Estou surpreso que ninguém tenha perguntado por que você está preocupado com o tipo real do valor de retorno dedoall
tar
Você pode converter para um vetor:(vec (take 3 (repeatedly rand)))
Kris

Respostas:

161

doallé tudo o que você precisa. Só porque seqtem tipo LazySeqnão significa que tenha avaliação pendente. Os preguiçosos seqarmazenam seus resultados em cache, portanto, tudo o que você precisa fazer é acompanhar o preguiçoso sequma vez (como doallfaz) para forçar tudo e, assim, torná-lo não preguiçoso. seqse não forçar a coleção inteira para ser avaliado.

Rich Hickey
fonte
2
Eu mudei isso para a resposta aceita. Em uma nota relacionada, por que meios você pode determinar se um LazySeq foi avaliado anteriormente?
Tim Clemons,
10
Eu acredito que você acabou de ligar realized?.
toofarsideways
1
Provavelmente deve haver uma realizeoperação correspondente realized?.
Reut Sharabani 01 de
Isso tudo é muito bom. Mas, uma vez que algumas funções como contains?não se importam se você percebeu ou não o preguiçoso seq, isso responde à pergunta específica conforme feita, mas menos ao título da pergunta.
matanster
75

Isso é até certo ponto uma questão de taxonomia. uma sequência preguiçosa é apenas um tipo de sequência, assim como uma lista, vetor ou mapa. Portanto, a resposta é, claro, "depende do tipo de sequência não preguiçosa que você deseja obter:
Escolha entre:

  • uma sequência preguiçosa ex-preguiçosa (totalmente avaliada) (doall ... )
  • uma lista para acesso sequencial (apply list (my-lazy-seq)) OR (into () ...)
  • um vetor para acesso aleatório posterior (vec (my-lazy-seq))
  • um mapa ou um conjunto se você tiver algum propósito especial.

Você pode ter qualquer tipo de sequência que mais atenda às suas necessidades.

Arthur Ulfeldt
fonte
Esta é a melhor resposta.
Felipe
4
A resposta aceita é tecnicamente correta, mas essa resposta foi muito útil para mim. Eu estava tentando mapear uma função em um vetor e depois cuspir os resultados em um arquivo e, mesmo depois de chamar doall, o arquivo continha "clojure.lang.LazySeq@address" em vez do conteúdo da sequência. Chamar vec no mapa de valor retornado me deu o que eu precisava cuspir para o arquivo.
Jesse Rosalia
1
@JesseRosalia É bom saber que a única resposta de Rich Hickey em todo o SO foi tecnicamente correta. ;-)
Phil Cooper
(vec (my-lazy-seq))não é tão bom em situações como as seguintes: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Visto que cheshireopta por produzir um seq preguiçoso de(json/parse-string)
codeasone
A mitigação para o acima foi usar ansioso(json/parse-string-strict)
codeasone em
22

Esse cara rico parece saber seu clojure e está absolutamente certo.
Mas acho que este snippet de código, usando seu exemplo, pode ser um complemento útil para esta pergunta:

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

Na verdade, o tipo não mudou, mas a realização mudou

Peter
fonte
2
É importante notar, porém, que você não precisa forçar a sequência inteira para realized?retornar true. Por exemplo(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
Esse cara rico: D haha
nimrod
10
@nimrod :) o trocadilho, entretanto, era para ser em "hís clojure".
Pedro
10
Para quem não sabe, "o cara rico" inventou Clojure.
erturne
1
@AlexCoventry, seu exemplo retorna[true true]
7

Eu tropecei nesta postagem do blog sobre doallnão ser recursivo. Para isso, descobri que o primeiro comentário no post funcionou. Algo na linha de:

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

Achei isso útil em um teste de unidade em que queria forçar a avaliação de alguns aplicativos aninhados mappara forçar uma condição de erro.

leeor
fonte
5
(.getClass (into '() (take 3 (repeatedly rand))))
estupito
fonte
3
Esta é uma ideia terrível. Ele reverte o seq de entrada.
amalloy de
3
Claro, neste caso, inverter a entrada não faz diferença, já que são apenas 3 números aleatórios .... :-)
mikera