collections clojure sequence

collections - Clojure: contras(seq) vs. conj(lista)



sequence (4)

Sé que los cons devuelven un seq y conj devuelve una colección. También sé que conj "agrega" el artículo al final óptimo de la colección, y los cons siempre "agregan" el elemento al frente. Este ejemplo ilustra ambos puntos:

user=> (conj [1 2 3] 4) //returns a collection [1 2 3 4] user=> (cons 4 [1 2 3]) //returns a seq (4 1 2 3)

Para vectores, mapas y conjuntos, estas diferencias tienen sentido para mí. Sin embargo, para las listas parecen idénticas.

user=> (conj (list 3 2 1) 4) //returns a list (4 3 2 1) user=> (cons 4 (list 3 2 1)) //returns a seq (4 3 2 1)

¿Hay algún ejemplo de uso de listas en las que conj cons exhiben comportamientos diferentes o son realmente intercambiables? Expresado de manera diferente, ¿hay algún ejemplo en el que una lista y un seq no puedan usarse de manera equivalente?


Entiendo que lo que dices es verdad: conj en una lista es equivalente a los contras en una lista.

Puede pensar que conj es una operación de "insertar en alguna parte" y es una operación de "insertar en la cabeza". En una lista, es más lógico insertar en la cabecera, por lo que conj y contras son equivalentes en este caso.


Otra diferencia es el comportamiento de la lista?

(list? (conj () 1)) ;=> true (list? (cons 1 ())) ; => false


Otra diferencia es que, debido a que conj toma una secuencia como primer argumento, juega muy bien con alter al actualizar una ref a alguna secuencia:

(dosync (alter a-sequence-ref conj an-item))

Esto básicamente hace (conj a-sequence-ref an-item) de una manera segura para subprocesos. Esto no funcionaría con los cons . Vea el capítulo sobre Concurrencia en la programación de Clojure por Stu Halloway para más información.


Una diferencia es que conj acepta cualquier número de argumentos para insertar en una colección, mientras que cons solo una:

(conj ''(1 2 3) 4 5 6) ; => (6 5 4 1 2 3) (cons 4 5 6 ''(1 2 3)) ; => IllegalArgumentException due to wrong arity

Otra diferencia está en la clase del valor de retorno:

(class (conj ''(1 2 3) 4)) ; => clojure.lang.PersistentList (class (cons 4 ''(1 2 3)) ; => clojure.lang.Cons

Tenga en cuenta que estos no son realmente intercambiables; en particular, clojure.lang.Cons no implementa clojure.lang.Counted , por lo que count con él ya no es una operación de tiempo constante (en este caso, probablemente se reduciría a 1 + 3 - el 1 proviene de un cruce lineal sobre el primer elemento, el 3 viene de (next (cons 4 ''(1 2 3)) siendo una PersistentList y por lo tanto Counted ).

La intención detrás de los nombres es, creo, que los cons significan contras (truct a seq) 1 , mientras que conj significa conj (oin un ítem en una colección). El seq se construye por cons comienza con el elemento pasado como su primer argumento y tiene como su parte next / rest lo que resulta de la aplicación de seq al segundo argumento; como se muestra arriba, todo es de la clase clojure.lang.Cons . Por el contrario, conj siempre devuelve una colección de aproximadamente el mismo tipo que la colección que se le pasó. (Aproximadamente, porque un PersistentArrayMap se convertirá en un PersistentHashMap tan pronto como crezca más allá de 9 entradas).

1 Tradicionalmente, en el mundo de Lisp, cons cons (tructuras un par), entonces Clojure se aparta de la tradición de Lisp al hacer que su función cons construya un seq que no tiene un cdr tradicional. El uso generalizado de cons para significar "construir un registro de algún tipo u otro para mantener una cantidad de valores en conjunto" es actualmente omnipresente en el estudio de los lenguajes de programación y su implementación; eso es lo que significa cuando se menciona "evitar el consing".