Clojure: cons (seq) vs. conj (Liste)

98

Ich weiß, dass dies conseine Sequenz und conjeine Sammlung zurückgibt. Ich weiß auch, dass conjder Artikel dem optimalen Ende der Sammlung cons"hinzugefügt" wird und der Artikel immer der Vorderseite "hinzugefügt" wird. Dieses Beispiel veranschaulicht diese beiden Punkte:

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)

Für Vektoren, Karten und Mengen sind diese Unterschiede für mich sinnvoll. Für Listen scheinen sie jedoch identisch zu sein.

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)

Gibt es Beispiele für Listen, bei denen conjvs. consunterschiedliche Verhaltensweisen aufweisen, oder sind sie wirklich austauschbar? Gibt es ein anderes Beispiel, in dem eine Liste und eine Sequenz nicht gleichwertig verwendet werden können?

dbyrne
quelle

Antworten:

150

Ein Unterschied besteht darin, dass conjeine beliebige Anzahl von Argumenten zum Einfügen in eine Sammlung akzeptiert wird, während consnur eines benötigt wird:

(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

Ein weiterer Unterschied besteht in der Klasse des Rückgabewerts:

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

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

Beachten Sie, dass diese nicht wirklich austauschbar sind. Insbesondere clojure.lang.Consnicht implementiert clojure.lang.Counted, so dass ein Ein countes nicht länger eine konstante Zeitoperation ist (in diesem Fall würde es wahrscheinlich auf 1 + 3 reduzieren - die 1 kommt von der linearen Durchquerung über das erste Element, die 3 kommt von (next (cons 4 '(1 2 3))einem PersistentListund also Counted).

Die Absicht hinter den Namen ist, glaube ich, das consheißt, eine Sequenz 1 zu konsultieren (zu konstruieren) , wohingegen conjbedeutet, einen Gegenstand auf eine Sammlung zu setzen. Die seqdurch konstruiert consbeginnt mit dem Element als erstes Argument übergeben und hat als ihre next/ restTeil resultierenden das Ding von der Anwendung seqzu dem zweiten Argument; Wie oben gezeigt, ist das Ganze von Klasse clojure.lang.Cons. Gibt dagegen conjimmer eine Sammlung vom ungefähr gleichen Typ zurück wie die an sie übergebene Sammlung. (Ungefähr, weil aus a ein PersistentArrayMapwird, PersistentHashMapsobald es über 9 Einträge hinaus wächst.)


1 Traditionell gibt es in der Lisp-Welt consNachteile (ein Paar), so dass Clojure von der Lisp-Tradition abweicht, indem seine consFunktion eine Sequenz konstruiert, die keine Tradition hat cdr. Die verallgemeinerte Verwendung von cons"Konstruieren eines Datensatzes irgendeiner Art, um eine Reihe von Werten zusammenzuhalten" ist derzeit beim Studium der Programmiersprachen und ihrer Implementierung allgegenwärtig. Das ist es, was gemeint ist, wenn "Vermeiden von Konsing" erwähnt wird.

Michał Marczyk
quelle
1
Was für eine fantastische Zusammenfassung! Mir war nicht bewusst, dass es einen Cons-Typ gab. Gut gemacht!
Daniel Yankowsky
Vielen Dank. Freut mich das zu hören. :-)
Michał Marczyk
2
Gibt übrigens als Sonderfall (cons foo nil)einen Singleton zurück PersistentList(und ebenfalls für conj).
Michał Marczyk
1
Eine weitere hervorragende Erklärung. Du bist wirklich ein Clojure Jedi!
Dbyrne
1
Nach meiner Erfahrung ist es wichtig, Listen als Listen und nicht als Sequenzen zu behandeln, wenn es auf die Leistung ankommt.
Cgrand
11

Mein Verständnis ist, dass das, was Sie sagen, wahr ist: Konj auf einer Liste entspricht den Nachteilen auf einer Liste.

Sie können sich Conj als Operation "Irgendwo einfügen" und Nachteile als Operation "Am Kopf einfügen" vorstellen. In einer Liste ist es am logischsten, am Kopf einzufügen, daher sind Konj und Contra in diesem Fall gleichwertig.

Daniel Yankowsky
quelle
8

Ein weiterer Unterschied besteht darin, dass conjeine Sequenz als erstes Argument verwendet wird, alterwenn sie auf eine refSequenz aktualisiert wird:

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

Dies geschieht grundsätzlich (conj a-sequence-ref an-item)threadsicher. Das würde nicht funktionieren cons. Weitere Informationen finden Sie im Kapitel über Parallelität in der Programmierung von Clojure von Stu Halloway.

user323818
quelle
2

Ein weiterer Unterschied ist das Verhalten der Liste?

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
FredAKA
quelle
4
Nachteile gibt immer eine Sequenz zurück, die konj den gleichen Typ wie die bereitgestellte zurückgibt
Ning Sun
-1

In der Tupelo-Bibliothek gibt es spezielle Funktionen zum Hinzufügen oder Anhängen von Werten zu einer sequentiellen Sammlung:

(append [1 2] 3  )   ;=> [1 2 3  ]
(append [1 2] 3 4)   ;=> [1 2 3 4]

(prepend   3 [2 1])  ;=> [  3 2 1]
(prepend 4 3 [2 1])  ;=> [4 3 2 1]
Alan Thompson
quelle