Cómo convertir una secuencia perezosa en no perezosa en Clojure

95

Intenté lo siguiente en Clojure, esperando que se devolviera la clase de una secuencia no perezosa:

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

Sin embargo, esto todavía regresa clojure.lang.LazySeq. Supongo que doallsí evalúa la secuencia completa, pero devuelve la secuencia original ya que sigue siendo útil para la memorización.

Entonces, ¿cuál es el medio idiomático de crear una secuencia no perezosa a partir de una perezosa?

Tim Clemons
fuente
Me sorprende que nadie haya preguntado por qué le preocupa el tipo real del valor de retorno dedoall
tar
Puede convertir a un vector:(vec (take 3 (repeatedly rand)))
Kris

Respuestas:

161

doalles todo lo que necesitas. El hecho de que el seqtipo has LazySeqno significa que tenga una evaluación pendiente. Los perezosos seqalmacenan en caché sus resultados, por lo que todo lo que necesita hacer es caminar el perezoso sequna vez (como lo doallhace) para forzarlo todo y, por lo tanto, convertirlo en no perezoso. seqno no forzar la colección entera a ser evaluado.

Rich Hickey
fuente
2
Cambié esto a la respuesta aceptada. En una nota relacionada, ¿por qué medios puede determinar si un LazySeq ha sido evaluado previamente?
Tim Clemons
10
Creo que solo llama realized?.
toofarsideways
1
Probablemente debería haber una realizeoperación que coincida realized?.
Reut Sharabani
Todo esto está muy bien. Pero dado que algunas funciones como contains?no importa si se dio cuenta de la secuencia perezosa o no, esto responde a la pregunta específica tal como se le preguntó, pero menos al título de la pregunta.
matanster
75

Hasta cierto punto, se trata de una cuestión de taxonomía. una secuencia perezosa es solo un tipo de secuencia como lo es una lista, vector o mapa. Entonces, la respuesta es, por supuesto, "depende del tipo de secuencia no perezosa que desee obtener:
elija entre:

  • una secuencia ex-perezosa (completamente evaluada) (doall ... )
  • una lista para acceso secuencial (apply list (my-lazy-seq)) OR (into () ...)
  • un vector para un posterior acceso aleatorio (vec (my-lazy-seq))
  • un mapa o un conjunto si tiene algún propósito especial.

Puede tener cualquier tipo de secuencia que se adapte a sus necesidades.

Arthur Ulfeldt
fuente
Esta es la mejor respuesta.
Felipe
4
La respuesta aceptada es técnicamente correcta, pero esta respuesta me resultó muy útil. Estaba tratando de mapear una función sobre un vector y luego escupir los resultados en un archivo, e incluso después de llamar a doall, el archivo contenía "clojure.lang.LazySeq@address" en lugar del contenido de la secuencia. Llamar a vec en el mapa de valor devuelto me dio lo que necesitaba escupir en el archivo.
Jesse Rosalia
1
@JesseRosalia Es bueno saber que la única respuesta de Rich Hickey en todo SO fue técnicamente correcta. ;-)
Phil Cooper
(vec (my-lazy-seq))no es tan agradable en situaciones como las siguientes: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Dado que cheshireelige producir una (json/parse-string)
secuencia
La mitigación para lo anterior fue usar ansioso(json/parse-string-strict)
codeasone
22

Este tipo rico parece conocer su clojure y tiene toda la razón.
Pero creo que este fragmento de código, usando su ejemplo, podría ser un complemento útil para esta pregunta:

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

De hecho, el tipo no ha cambiado, pero la realización ha

Pedro
fuente
2
Sin embargo, vale la pena señalar que no es necesario forzar la secuencia completa para realized?regresar true. Por ejemplo(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
Este tipo rico: D jaja
nimrod
10
@nimrod :) Sin embargo, el juego de palabras estaba destinado a estar en "hís clojure".
Peter
10
Para aquellos que no lo saben, "el chico rico" inventó Clojure.
erturne
1
@AlexCoventry su ejemplo devuelve[true true]
7

Me encontré con esta publicación de este blog sobre doallno ser recursivo. Por eso encontré que el primer comentario en la publicación hizo el truco. Algo parecido a:

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

Encontré esto útil en una prueba unitaria en la que quería forzar la evaluación de algunas aplicaciones anidadas mappara forzar una condición de error.

leeor
fuente
5
(.getClass (into '() (take 3 (repeatedly rand))))
estupito
fuente
3
Esta es una idea terrible. Invierte la secuencia de entrada.
amalloy
3
Por supuesto, en este caso, invertir la entrada no hace ninguna diferencia, ya que son solo 3 números aleatorios .... :-)
mikera