Comment convertir une séquence paresseuse en séquence non paresseuse dans Clojure

95

J'ai essayé ce qui suit dans Clojure, en m'attendant à ce que la classe d'une séquence non paresseuse soit renvoyée:

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

Cependant, cela revient toujours clojure.lang.LazySeq. Je suppose que doallcela évalue la séquence entière, mais renvoie la séquence d'origine car elle est toujours utile pour la mémorisation.

Alors, quel est le moyen idiomatique de créer une séquence non paresseuse à partir d'une séquence paresseuse?

Tim Clemons
la source
Je suis surpris que personne ne vous ait demandé pourquoi vous êtes préoccupé par le type réel de la valeur de retour dedoall
tar
Vous pouvez convertir en vecteur:(vec (take 3 (repeatedly rand)))
Kris

Réponses:

161

doallest tout ce dont vous avez besoin. Ce n'est pas parce que le seqtype a LazySeqest en attente d'évaluation. Lazy met en seqcache leurs résultats, donc tout ce que vous avez à faire est de marcher sequne fois sur le paresseux (comme le doallfait) afin de tout forcer, et ainsi le rendre non paresseux. seqne force pas l'évaluation de la collection entière.

Rich Hickey
la source
2
J'ai changé cela en réponse acceptée. Sur une note connexe, par quels moyens pouvez-vous déterminer si un LazySeq a déjà été évalué?
Tim Clemons
10
Je crois que vous venez d'appeler realized?.
toofarsideways
1
Il devrait probablement y avoir une realizeopération correspondante realized?.
Reut Sharabani
Tout cela est très agréable. Mais comme certaines fonctions comme contains?ne se soucient pas de savoir si vous avez réalisé le seq paresseux ou non, cela répond à la question spécifique telle que posée, mais moins au titre de la question.
matanster
75

C'est dans une certaine mesure une question de taxonomie. une séquence paresseuse n'est qu'un type de séquence, tout comme une liste, un vecteur ou une carte. Donc, la réponse est bien sûr "cela dépend du type de séquence non paresseux que vous souhaitez obtenir: faites
votre choix parmi:

  • une séquence paresseuse ex-paresseuse (entièrement évaluée) (doall ... )
  • une liste d'accès séquentiel (apply list (my-lazy-seq)) OR (into () ...)
  • un vecteur pour un accès aléatoire ultérieur (vec (my-lazy-seq))
  • une carte ou un ensemble si vous avez un but spécial.

Vous pouvez avoir n'importe quel type de séquence qui répond à vos besoins.

Arthur Ulfeldt
la source
C'est la meilleure réponse.
Felipe
4
La réponse acceptée est techniquement correcte, mais cette réponse m'a été très utile. J'essayais de mapper une fonction sur un vecteur, puis de cracher les résultats dans un fichier, et même après avoir appelé doall, le fichier contenait "clojure.lang.LazySeq@address" au lieu du contenu de la séquence. L'appel de vec sur la valeur de la carte m'a permis d'obtenir ce dont j'avais besoin pour cracher dans le fichier.
Jesse Rosalia
1
@JesseRosalia Il est bon de savoir que la seule et unique réponse de Rich Hickey dans tout SO était techniquement correcte. ;-)
Phil Cooper
(vec (my-lazy-seq))n'est pas si agréable dans des situations comme celles-ci: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Depuis cheshirechoisit de produire un lazy-seq de(json/parse-string)
codeasone
L'atténuation pour ce qui précède était d'utiliser hâte(json/parse-string-strict)
codeasone
22

Ce type riche semble connaître son clojure et a tout à fait raison.
Mais je pense que cet extrait de code, en utilisant votre exemple, pourrait être un complément utile à cette question:

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

En effet le type n'a pas changé mais la réalisation a

Peter
la source
2
Il convient de noter, cependant, que vous n'avez pas besoin de forcer toute la séquence pour realized?revenir true. Par exemple(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
This Rich guy: D haha
nimrod
10
@nimrod :) le jeu de mots était cependant censé être dans "son clojure".
Peter
10
Pour ceux qui ne savent pas, "le gars riche" a inventé Clojure.
erturne
1
@AlexCoventry votre exemple renvoie[true true]
7

Je suis tombé sur ce billet de blog sur le fait de doallne pas être récursif. Pour cela, j'ai trouvé que le premier commentaire dans le post faisait l'affaire. Quelque chose du genre:

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

J'ai trouvé cela utile dans un test unitaire où je voulais forcer l'évaluation de certaines applications imbriquées mappour forcer une condition d'erreur.

leeor
la source
5
(.getClass (into '() (take 3 (repeatedly rand))))
stupito
la source
3
C'est une idée terrible. Il inverse la séquence d'entrée.
amalloy
3
Bien sûr, dans ce cas, l'inversion de l'entrée ne fait aucune différence, car ce ne sont que 3 nombres aléatoires .... :-)
mikera