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


98

Eu sei que consretorna um seq e conjretorna uma coleção. Também sei que conj"adiciona" o item ao final ideal da coleção e conssempre "adiciona" o item à frente. Este exemplo ilustra esses dois pontos:

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 vetores, mapas e conjuntos, essas diferenças fazem sentido para mim. No entanto, para listas, eles parecem idênticos.

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)

Existem exemplos de uso de listas em que conjvs. consexibem comportamentos diferentes ou são verdadeiramente intercambiáveis? Com uma frase diferente, há um exemplo em que uma lista e um seq não podem ser usados ​​de forma equivalente?

Respostas:


150

Uma diferença é que conjaceita qualquer número de argumentos para inserir em uma coleção, enquanto consleva apenas um:

(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

Outra diferença está na classe do valor de retorno:

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

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

Observe que eles não são realmente intercambiáveis; em particular, clojure.lang.Consnão implementa clojure.lang.Counted, então a counton ele não é mais uma operação de tempo constante (neste caso, provavelmente seria reduzido para 1 + 3 - o 1 vem do percurso linear sobre o primeiro elemento, o 3 vem de (next (cons 4 '(1 2 3))ser um PersistentListe portanto Counted).

A intenção por trás dos nomes é, creio eu, que conssignifica contr (construir a seq) 1 , ao passo que conjsignifica conj (inserir um item em uma coleção). A seqsendo construído por conscomeça com o elemento passado como primeiro argumento e tem como next/ restparte a coisa resultante da aplicação de seqpara o segundo argumento; como mostrado acima, tudo é de classe clojure.lang.Cons. Em contraste, conjsempre retorna uma coleção mais ou menos do mesmo tipo da coleção passada a ele. (Grosso modo, porque a PersistentArrayMapserá transformado em PersistentHashMapassim que crescer além de 9 entradas.)


1 Tradicionalmente, no mundo Lisp, conscons (constrói um par), então Clojure se afasta da tradição Lisp ao ter sua consfunção construir um seq que não possui um tradicional cdr. O uso generalizado de conspara significar "construir um registro de algum tipo ou outro para manter uma série de valores juntos" é atualmente onipresente no estudo de linguagens de programação e sua implementação; é isso que se quer dizer quando se menciona "evitar golpes".


1
Que artigo fantástico! Eu não sabia que havia um tipo de Cons. Bem feito!
Daniel Yankowsky

Obrigado. Feliz de ouvir isso. :-)
Michał Marczyk

2
A propósito, como um caso especial, (cons foo nil)retorna um singleton PersistentList(e da mesma forma para conj).
Michał Marczyk

1
Outra explicação excelente. Você realmente é um clojure jedi!
dbyrne

1
Em minha experiência, tratar listas como listas e não como sequências é importante quando o desempenho é importante.
cgrand de

11

Meu entendimento é que o que você diz é verdade: conj em uma lista é equivalente a contras em uma lista.

Você pode pensar em conj como sendo uma operação de "inserir em algum lugar" e os contras como uma operação de "inserir na cabeça". Em uma lista, é mais lógico inserir no cabeçalho, então conj e contras são equivalentes neste caso.


8

Outra diferença é que, como conjleva uma sequência como primeiro argumento, funciona bem com a alteratualização de a refpara alguma sequência:

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

Basicamente, isso ocorre (conj a-sequence-ref an-item)de maneira segura para threads. Isso não funcionaria com cons. Veja o capítulo Concurrency in Programming Clojure de Stu Halloway para mais informações.


2

Outra diferença é o comportamento da lista?

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

4
cons sempre retorna uma sequência que conj retorna o mesmo tipo daquele fornecido
Ning Dom

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.