Como entender o preguiçoso-seq do clojure
Estou tentando entender o clojurelazy-seq
operador e o conceito de avaliação preguiçosa em geral. Conheço a idéia básica por trás do conceito: a avaliação de uma expressão é adiada até que o valor seja necessário.
Em geral, isso é possível de duas maneiras:
em tempo de compilação usando macros ou formulários especiais;em tempo de execução usando funções lambdaCom técnicas de avaliação lenta, é possível construir estruturas de dados infinitas que são avaliadas como consumidas. Essas seqüências infinitas utilizam lambdas, fechamentos e recursão. No clojure, essas estruturas de dados infinitas são geradas usandolazy-seq
econs
formulários.
Eu quero entender comolazy-seq
isso é mágico? Eu sei que é realmente uma macro. Considere o seguinte exemplo.
(defn rep [n]
(lazy-seq (cons n (rep n))))
Aqui orep
A função retorna uma sequência do tipo preguiçosamente avaliadaLazySeq
, que agora podem ser transformados e consumidos (assim avaliados) usando a API de sequência. Esta API fornece funçõestake
, map
, filter
ereduce
.
No formato expandido, podemos ver como o lambda é utilizado para armazenar a receita da célula sem avaliá-la imediatamente.
(defn rep [n]
(new clojure.lang.LazySeq (fn* [] (cons n (rep n)))))
Mascomo a API de sequência realmente funciona comLazySeq
?O que realmente acontece na seguinte expressão?(reduce + (take 3 (map inc (rep 5))))
map
aplicado à sequência,como é quetake
limitar a sequência ecomo funciona o terminalreduce
avaliar a sequência?Além disso,como essas funções funcionam com umVector
ou umLazySeq
?
Além disso,é possível gerar estruturas de dados infinitas aninhadas?: lista contendo listas, contendo listas, contendo listas ... indo infinitamente ampla e profunda, avaliada como consumida com a API de sequência?
E última pergunta,existe alguma diferença prática entre esse
(defn rep [n]
(lazy-seq (cons n (rep n))))
e isto?
(defn rep [n]
(cons n (lazy-seq (rep n))))