Clojure: проти (conq) проти conj (список)


98

Я знаю, що consповертає послідовність і 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)

Чи є приклади, що використовують списки, де conjпроти consпроявляють різні форми поведінки, чи вони справді взаємозамінні? Фразоване по-різному, чи є приклад, коли список і послідовність не можуть використовуватися рівномірно?

Відповіді:


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, так що countна ньому більше не операція постійного часу (в даному випадку це, ймовірно , зводиться до 1 + 3 - 1 походить від лінійного обходу поверх першого елемент, 3 походить від (next (cons 4 '(1 2 3))того , щоб бути PersistentListі таким чином Counted).

Намір, що стоїть за іменами, я вважаю, що це consозначає "протистояти послідовності" 1 , тоді як conjозначає конфігувати (в предметі до колекції). Будова, seqщо будується шляхом, consпочинається з елемента, переданого як його перший аргумент, і є його next/ restчастиною річ, що виникає в результаті застосування seqдо другого аргументу; як показано вище, вся справа в класі clojure.lang.Cons. На відміну від цього, conjзавжди повертає колекцію приблизно того ж типу, що і колекція, передана їй. (Приблизно тому, що засіб PersistentArrayMapбуде перетворено на показник, PersistentHashMapяк тільки він перевищить 9 записів.)


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


1
Яка фантастична написання! Мені не було відомо, що є тип «Мінус». Молодці!
Даніель Янковський

Дякую. Радий це чути. :-)
Michał Marczyk

2
Між іншим, як особливий випадок (cons foo nil)повертає сингл PersistentList(і так само conj).
Michał Marczyk

1
Ще одне чудове пояснення. Ви справді є джеджі-клоджуром!
dbyrne

1
На моєму досвіді, трактування списків як списків, а не як послідовностей - це важливо, коли результативність має значення.
cgrand

11

Я розумію, що те, що ви говорите, є істинним: conj у списку еквівалентний мінусам у списку.

Ви можете вважати, що conj є операцією "вставити десь", а мінусом - як операцією "вставити в голову". У списку найбільш логічно вставити на чолі, тому conj і мінуси в цьому випадку рівноцінні.


8

Інша відмінність полягає в тому, що, оскільки conjприймає послідовність як перший аргумент, вона добре поєднується з alterоновленням refдо певної послідовності:

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

Це в основному (conj a-sequence-ref an-item)безпечно для потоків. Це не працює cons. Докладнішу інформацію див. У розділі «Конкурс у програмуванні Clojure » Стю Халлоуей.


2

Ще одна відмінність - поведінка списку?

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

4
мінуси завжди повертає послідовність, яка conj повертає той самий тип наданої
Ning Sun

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.