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:
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?
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".
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.
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.
(cons foo nil)
retorna um singletonPersistentList
(e da mesma forma paraconj
).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.
fonte
Outra diferença é que, como
conj
leva uma sequência como primeiro argumento, funciona bem com aalter
atualização de aref
para alguma sequência:Basicamente, isso ocorre
(conj a-sequence-ref an-item)
de maneira segura para threads. Isso não funcionaria comcons
. Veja o capítulo Concurrency in Programming Clojure de Stu Halloway para mais informações.fonte
Outra diferença é o comportamento da lista?
fonte
Existem funções dedicadas na Biblioteca Tupelo para adicionar valores anexar ou anexar a qualquer coleção sequencial:
fonte