Clojure: contro (seq) contro conj (lista)


98

So che consrestituisce una sequenza e conjrestituisce una raccolta. So anche che conj"aggiunge" l'articolo alla fine ottimale della collezione e cons"aggiunge" sempre l'articolo in primo piano. Questo esempio illustra entrambi questi punti:

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)

Per i vettori, le mappe e gli insiemi queste differenze hanno senso per me. Tuttavia, per gli elenchi sembrano identici.

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)

Ci sono esempi che utilizzano elenchi in cui conjvs. consesibiscono comportamenti diversi o sono veramente intercambiabili? Espressa in modo diverso, c'è un esempio in cui una lista e una sequenza non possono essere usate in modo equivalente?

Risposte:


150

Una differenza è che conjaccetta un numero qualsiasi di argomenti da inserire in una raccolta, mentre ne consrichiede solo uno:

(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

Un'altra differenza è nella classe del valore restituito:

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

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

Nota che questi non sono realmente intercambiabili; in particolare, clojure.lang.Consnon implementa clojure.lang.Counted, quindi a countsu non è più un'operazione a tempo costante (in questo caso si ridurrebbe probabilmente a 1 + 3 - l'1 deriva dall'attraversamento lineare sul primo elemento, il 3 deriva (next (cons 4 '(1 2 3))dall'essere a PersistentListe quindi Counted).

L'intenzione dietro i nomi è, credo, che conssignifichi costruire un seq 1 , mentre conjsignifica congiungere un oggetto a una collezione. Il seqessendo costruito da consinizia con l'elemento passato come primo argomento e ha come next/ restparte la cosa risultante dall'applicazione seqal secondo argomento; come mostrato sopra, l'intera cosa è di classe clojure.lang.Cons. Al contrario, conjrestituisce sempre una raccolta approssimativamente dello stesso tipo della raccolta passata. (Approssimativamente, perché a PersistentArrayMapsarà trasformato in a PersistentHashMapnon appena supera le 9 voci.)


1 Tradizionalmente, nel mondo Lisp, conscostituisce una coppia, quindi Clojure si discosta dalla tradizione Lisp avendo la sua consfunzione di costruire un seq che non ha un tradizionale cdr. L'uso generalizzato di consper significare "costruire un record di un tipo o di un altro per tenere insieme un certo numero di valori" è attualmente onnipresente nello studio dei linguaggi di programmazione e della loro implementazione; questo è ciò che si intende quando si parla di "evitare il consumo".


1
Che fantastica recensione! Non sapevo che ci fosse un tipo Contro. Molto bene!
Daniel Yankowsky

Grazie. Sono felice di sentirlo. :-)
Michał Marczyk

2
Per inciso, come caso speciale, (cons foo nil)restituisce un singleton PersistentList(e allo stesso modo per conj).
Michał Marczyk

1
Un'altra superba spiegazione. Sei veramente un clojure jedi!
dbyrne

1
Nella mia esperienza, trattare gli elenchi come elenchi e non come sequenze è importante quando le prestazioni sono importanti.
cgrand

11

La mia comprensione è che ciò che dici è vero: conj su una lista equivale a contro su una lista.

Si può pensare a conj come un'operazione di "inserimento da qualche parte" e contro come un'operazione di "inserimento in testa". In un elenco, è più logico inserire all'inizio, quindi in questo caso congi e contro sono equivalenti.


8

Un'altra differenza è che poiché conjprende una sequenza come primo argomento, funziona bene alterquando si aggiorna refa una sequenza:

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

Questo fondamentalmente avviene (conj a-sequence-ref an-item)in modo thread-safe. Questo non funzionerebbe con cons. Vedere il capitolo sulla concorrenza nella programmazione di Clojure di Stu Halloway per maggiori informazioni.


2

Un'altra differenza è il comportamento della lista?

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

4
cons restituisce sempre una sequenza che conj restituisce lo stesso tipo di quella fornita
Ning Sun

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.