(conj collection item)
agrega item
a collection
. Para hacer eso, necesita darse cuenta collection
. (Explicaré por qué a continuación.) Por lo tanto, la llamada recursiva ocurre inmediatamente, en lugar de diferir.
(cons item collection)
crea una secuencia que comienza con item
, seguido de todo en collection
. Significativamente, es no necesita realizar collection
. Entonces la llamada recursiva será aplazada (debido al uso de lazy-seq
) hasta que alguien intente obtener la cola de la secuencia resultante.
Voy a explicar cómo funciona internamente:
cons
realidad devuelve un objeto clojure.lang.Cons
, que es lo secuencias perezosos están hechos. conj
devuelve el mismo tipo de colección que la pasa (ya sea una lista, un vector o cualquier otra cosa). conj
hace esto utilizando una llamada de método polimórfico Java en la colección en sí. (Consulte line 524 of clojure/src/jvm/clojure/lang/RT.java
.)
¿Qué sucede cuando ocurre esa llamada al método Java en el objeto clojure.lang.LazySeq
que se devuelve por lazy-seq
? (La forma en que los objetos Cons
y LazySeq
trabajan juntos para formar secuencias perezosas será más clara a continuación). Consulte line 98 of clojure/src/jvm/clojure/lang/LazySeq.java
. Observe que llama a un método llamado seq
. Esto es lo que se da cuenta del valor de LazySeq
(salte a line 55 para obtener más información).
Por lo tanto, podría decir que conj
necesita saber exactamente qué tipo de colección lo pasó, pero cons
no lo hace. cons
solo requiere que el argumento "recopilación" sea ISeq
.
Tenga en cuenta que Cons
objetos en Clojure son diferentes de "células cons" en otros Lisps - en la mayoría de Lisp, un "contras" es simplemente un objeto que tiene 2 punteros a otros objetos arbitrarios. De modo que puede usar células de cons para construir árboles, y así sucesivamente. Un Clojure Cons
toma un Object
arbitrario como cabeza, y un ISeq
como cola. Dado que Cons
implementa ISeq
, puede construir secuencias de objetos Cons
, pero también pueden indicar vectores, listas, etc. (Tenga en cuenta que una "lista" en Clojure es un tipo especial (PersistentList
), y es no construido a partir de objetos Cons
) clojure.lang.LazySeq
también implementa ISeq
, por lo que se puede utilizar como la cola ("cdr" en Lisps) de un Cons
.Un LazySeq
contiene una referencia a algún código que evalúa de algún tipo, pero en realidad no evalúa ese código hasta que sea necesario, y después de que evalúa el código, almacena en caché el ISeq
devuelto y lo delega.
... ¿todo esto empieza a tener sentido? ¿Tienes la idea de cómo funcionan las secuencias perezosas? Básicamente, empiezas con LazySeq
. Cuando se realiza el LazySeq
, se evalúa como Cons
, que apunta a otro LazySeq
. Cuando ese se realiza ... entiendes la idea. Entonces obtienes una cadena de objetos LazySeq
, cada tenencia (y delegando a) un Cons
.
Acerca de la diferencia entre "conses" y "listas" en Clojure, "listas" (objetos PersistentList
) contienen un campo "longitud" en caché, por lo que pueden responder a count
en O (1) hora. Esto no funcionaría en otros Lisps, porque en la mayoría de Lisps, las "listas" son mutables. Pero en Clojure son inmutables, por lo que el almacenamiento en caché funciona.
Cons
objetos en Clojure No tienen una longitud caché - si lo hicieran, ¿cómo podrían ser utilizados para implementar secuencias perezosos (e incluso infinitos)? Si intenta tomar el count
de Cons
, simplemente llama a count
en la cola, y luego incrementa el resultado en 1.
Para otros que vienen aquí: mi respuesta a continuación es muy detallada (quizás demasiado detallada) y puede ser confuso. Permítanme repetir el punto principal aquí: la semántica de 'conj' requiere que varíe su comportamiento en función del tipo de colección. Eso requiere el uso de una llamada al método (polimórfico) en el objeto de colección. Un 'LazySeq' maneja esa llamada de método delegando en su valor interno, que requiere realizar ese valor interno. Por el contrario, la semántica de los contras no requiere que invoque ningún método en la colección; solo tiene que almacenarlo en un campo de un objeto 'Contras'. –
Los últimos tres párrafos de la respuesta son particularmente útiles. Algunas personas pueden querer leerlas primero. – Mars