Uso de secuencia diferida de Clojure
functional-programming lazy-evaluation (1)
Una llamada lazy-seq simplemente ejecuta el cuerpo una vez que se accede a ella, luego se almacena en caché y devuelve el mismo resultado cada vez que se vuelve a llamar en el futuro.
Si desea usar esto para construir secuencias largas (o incluso infinitas), entonces debe anidar recursivamente otras llamadas de lazy-seq en la secuencia devuelta. Aquí está el caso más simple que puedo pensar:
(defn ints-from [n]
(cons n (lazy-seq (ints-from (inc n)))))
(take 10 (ints-from 7))
=> (7 8 9 10 11 12 13 14 15 16)
Cualquier llamada (ints-from) produce una secuencia que comienza con n, seguida de una secuencia perezosa de (ints-from (inc n)). Es una lista infinita, pero eso no es un problema porque lazy-seq asegura que (int-from (inc n)) solo se invoque cuando sea necesario. Podrías probar exactamente el mismo código sin lazy-seq y obtendrías un StackOverflowError muy rápidamente.
lazy-seq es solo una de las muchas formas posibles de crear secuencias diferidas, y a menudo no es la más conveniente. Las siguientes son algunas otras maneras interesantes / útiles para crear secuencias perezosas:
; range is an easy way to get an infinite lazy sequence of integers, starting with zero
(take 10 (range))
=> (0 1 2 3 4 5 6 7 8 9)
; map produces lazy sequences, so the following is lazy
(take 10 (map #(* % %) (range)))
=> (0 1 4 9 16 25 36 49 64 81)
; iterate is a good way of making infinite sequenes of the form x, f(x), f(f(x)).....
(take 10 (iterate (partial * 2) 1))
=> (1 2 4 8 16 32 64 128 256 512)
Tengo problemas para entender cómo se crea una secuencia perezosa en Clojure.
La documentación para la macro no está nada clara para mí:
Uso: (lazy-seq & body) Toma un cuerpo de expresiones que devuelve un ISeq o nil, y produce un objeto Seqable que invocará el cuerpo solo la primera vez que se llama a seq, y guardará en caché el resultado y lo devolverá en todos los siguientes llamadas seq.
Todos los ejemplos que he visto, parecen hacer algo como lo siguiente:
; return everything in the sequence starting at idx n
(defn myseq-after-n [n]
(...)
)
(def my-lazy-seq
(lazy-seq (conj [init-value] (myseq-after-n 2)))
)
Entonces, lo primero que no entiendo es que, dado que lazy-seq está fuera del llamado a conj, ¿cómo impide que conj genere una secuencia infinita en la evaluación?
Mi segunda pregunta es, ¿las definiciones de secuencia diferida siempre toman esta forma general?