Clojure: cons (seq) vs. cons (список)

98

Я знаю, что consвозвращает seq и conjвозвращает коллекцию. Я также знаю, что conj«добавляет» элемент в оптимальный конец коллекции и consвсегда «добавляет» элемент в начало. Этот пример иллюстрирует оба этих момента:

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)

Для векторов, карт и наборов эти различия имеют для меня смысл. Однако для списков они кажутся идентичными.

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)

Есть ли примеры использования списков, в которых conjvs. consдемонстрируют разное поведение, или они действительно взаимозаменяемы? Другими словами, есть ли пример, в котором список и последовательность не могут использоваться одинаково?

Dbyrne
источник

Ответы:

150

Одно отличие состоит в том, что conjпринимает любое количество аргументов для вставки в коллекцию, а consпринимает только один:

(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

Еще одно отличие заключается в классе возвращаемого значения:

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

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

Обратите внимание, что они на самом деле не взаимозаменяемы; в частности, clojure.lang.Consне реализует clojure.lang.Counted, поэтому a counton больше не является операцией с постоянным временем (в этом случае он, вероятно, уменьшится до 1 + 3 - 1 происходит от линейного обхода по первому элементу, 3 происходит от (next (cons 4 '(1 2 3))того, чтобы быть a PersistentListи таким образом Counted).

Я полагаю, что consза этими именами стоит намерение cons (построить последовательность) 1 , тогда как conjозначает объединить элемент в коллекцию. seqСтроится путем consначинается с элементом , переданным в качестве первого аргумента , и имеют в качестве своегоnext / restчасти вещи , в результате применения seqко второму аргументу; как показано выше, все это классно clojure.lang.Cons. Напротив, conjвсегда возвращает коллекцию примерно того же типа, что и переданная ей коллекция. (Примерно потому, что a PersistentArrayMapбудет преобразовано в a, PersistentHashMapкак только оно превысит 9 записей.)


1 Традиционно в мире Лиспа conscons (исправляет пару), поэтому Clojure отходит от традиции Лиспа в том, что его consфункция создает последовательность, не имеющую традиционного cdr. Обобщенное использование слова cons«создать запись того или иного типа для хранения нескольких значений вместе» в настоящее время повсеместно используется при изучении языков программирования и их реализации; это то, что имеется в виду, когда упоминается «избегание совершения».

Михал Марчик
источник
1
Какая фантастическая запись! Я не знал, что есть тип против. Отлично сработано!
Daniel Yankowsky
Спасибо. Рад слышать это. :-)
Michał Marczyk
2
Кстати, как особый случай, (cons foo nil)возвращает синглтон PersistentList(и аналогично для conj).
Michał Marczyk,
1
Еще одно прекрасное объяснение. Вы действительно настоящий джедай!
dbyrne 09
1
По моему опыту, обработка списков как списков, а не последовательностей важна, когда важна производительность.
cgrand
11

Насколько я понимаю, то, что вы говорите, правда: cons в списке эквивалентно cons в списке.

Вы можете думать о конъюнктуре как об операции "вставить куда-нибудь", а о минусе как о операции "вставки в начало". В списке логичнее всего вставлять в начале, поэтому в этом случае "минусы" и "минусы" эквивалентны.

Даниэль Янковский
источник
8

Другое отличие состоит в том, что, поскольку conjв качестве первого аргумента используется последовательность, она хорошо работает alterпри обновлении a refдо некоторой последовательности:

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

Это в основном делает (conj a-sequence-ref an-item)потокобезопасным способом. Это не сработает с cons. См. Главу «Параллелизм в программировании Clojure » Стю Хэллоуэя для получения дополнительной информации.

user323818
источник
2

Еще одно отличие - это поведение списка?

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
ФредАКА
источник
4
cons всегда возвращает последовательность, которая возвращает тот же тип, что и предоставленный
Ning Sun