Коли в Clojure, коли я повинен використовувати вектор над списком, і навпаки?


147

Я читав, що Вектори - це не послідовність, але Списки є. Я не впевнений, що обґрунтовує використання одного над іншим. Здається, вектори використовуються найбільше, але чи є в цьому причина?


Відповіді:


112

Ще раз, здається, я відповів на власне запитання, нетерплячи і задавши його в #clojure на Freenode. Добре, що відповідати на власні запитання рекомендується на Stackoverflow.com: D

У мене було швидке обговорення з Річ Хікі, і ось суть цього.

[12:21] <Raynes>    Vectors aren't seqs, right?
[12:21] <rhickey>   Raynes: no, but they are sequential
[12:21] <rhickey>   ,(sequential? [1 2 3])
[12:21] <clojurebot>    true
[12:22] <Raynes>    When would you want to use a list over a vector?
[12:22] <rhickey>   when generating code, when generating back-to-front
[12:23] <rhickey>   not too often in Clojure

Поки ви перебуваєте на екрані, перейдіть до темної сторони та приєднуйтесь до #stackoverflow! :-P
Кріс Єстер-Янг

Я насправді там простоював. Я перемикав клієнтів IRC і ніколи не думав додавати #stackoverflow у свій список автоматичного приєднання.
Рейне

Я новачок у Ліспі, але мені було цікаво, чи порушують вектори, карти та набори якимось чином ідею про те, що весь код взаємозамінний з даними? Або це лише одна з тих речей, що робить Clojure практичним Lisp? (АБО, чи можете ви оцінити вектор?)
Роб Грант

23
Це абсолютно не корисний фрагмент чату. "Генерування коду" "генерування спина до фронту" -> означає точно ?? У мене справді виникають труднощі з цим питанням, тому що в моїй книзі лінь + декларативний стиль = набагато краща ефективність, і все-таки вектори пропонуються скрізь у Clojure, що залишає мене зовсім заплутаним.
Джиммі Хоффа

22
@JimmyHoffa Наскільки я це розумію: "Генеруючий код" = "Всередині макроса" (тому що більшість коду - це виклики функцій, таким чином, списки); "генерація назад до фронту" = "побудова послідовності заздалегідь".
omiel

87

Якщо ви багато працювали з програмуванням на Java і знаєте рамки колекції Java, придумайте такі списки, як LinkedListвектори ArrayList. Таким чином, ви можете майже вибрати контейнери таким же чином.

Для подальшого уточнення: якщо ви збираєтесь додавати елементи окремо на передню або задню частину послідовності багато, пов'язаний список набагато краще, ніж вектор, тому що елементи не потрібно переміщувати навколо кожного разу. Однак якщо ви хочете часто потрапляти на конкретні елементи (не поруч із передньою чи задньою частинами списку) (тобто випадковий доступ), вам потрібно буде використовувати вектор.

До речі, вектори можна легко перетворити на сліди.

user=> (def v (vector 1 2 3))
#'user/v
user=> v
[1 2 3]
user=> (seq v)
(1 2 3)
user=> (rseq v)
(3 2 1)

Вектори не є послідовностями, але вони є послідовними. (Джерело: Розбагатіти на #clojure на freenode.) Також я зовсім не знаю Java, але Річ просто відповів на моє запитання.
Рейне

1
Я відредагую свій пост, щоб сказати, що вектори можна перетворити на seqs через функцію seq. :-)
Кріс Єстер-Янг

2
Вибрав свою відповідь, оскільки вона справді відповідала на запитання, і мені дуже не подобається обирати власні відповіді як правильні. Не здається правильним. Дякую. :)
Rayne

Деке краще, ніж пов'язаний список у разі додавання першого та останнього. LL досить жахливі: P
бокс

1
@boxed Ви не можете реалізовувати декор над вектором або ArrayListбез ефективного перевтілення ArrayDequeсебе.
Кріс Єстер-Янг

43

Вектори мають O (1) випадкові часи доступу, але їх потрібно попередньо розподілити. Списки можна динамічно розширювати, але доступ до випадкового елемента є O (n).


3
Технічно пов'язані списки мають O (1) час доступу ... якщо ви маєте доступ лише до переднього або заднього елемента. :-P Однак, вектори мають O (1) випадковий доступ. :-)
Кріс Єстер-Янг

4
("Зв'язаний список", як описано вище, посилається на подвійно пов'язані списки. Одномісно пов'язані списки мають доступ до O (1) лише до переднього елемента. :-P)
Chris Jester-Young

1
Оскільки хтось просто занурився у Clojure, це відповідь НАШЛИШЕ кращого, ніж інші два, які мають більше голосів. Інші двоє не кажуть мені нічого корисного.
keithjgrant

@ ChrisJester-Young Однопов'язаний список може підтримувати O (1) доступ до задньої частини, якщо він зберігає посилання на задній елемент, як це .
Gill Bates

30

Коли використовувати вектор:

  • Індексована продуктивність доступу - Ви отримуєте ~ O (1) вартість індексованого доступу порівняно з O (n) для списків
  • Додавання - з conj є ~ O (1)
  • Зручне позначення - мені здається, що простіше набирати та читати [1 2 3], ніж «(1 2 3) для буквального списку за обставин, коли будь-яка робота буде працювати.

Коли використовувати список:

  • Коли ви хочете отримати доступ до нього як послідовність (оскільки списки безпосередньо підтримують послідовність без виділення нових об'єктів)
  • Попередньо - додавання до початку списку з мінусами або, переважно, conj є O (1)

3
Навіть при додаванні / видаленні з обох кінців список є досить жахливим вибором. Деке набагато краще (в процесорі і особливо в пам'яті). Спробуйте github.com/pjstadig/deque-clojure
коробка

2
Щодо: ~O(1)для тих, кому це пояснення витрат може бути корисним - stackoverflow.com/questions/200384/constant-amortized-time
Мерлін Морган-Грем

13

лише швидка сторона:

"Я читав, що" Вектори "не є послідовностями, але списки є". 

послідовності є більш загальними, ніж списки чи вектори (або карти, або набори). Прикро,
що REPL друкує списки та послідовності однакові, тому що це справді робить його схожим на списки - це послідовності, хоча вони різні. функція (seq) створить послідовність з безлічі різних речей, включаючи списки, і ви зможете подавати цю послідовність в будь-яку з безлічі функцій, які роблять вишукані речі з допомогою послідовностей.

user> (class (list 1 2 3))
clojure.lang.PersistentList

user> (class (seq (list 1 2 3)))
clojure.lang.PersistentList

user> (class (seq [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq

У Sec є ярлик, який повертає його аргумент, якщо це вже послідовно:

user> (let [alist (list 1 2 3)] (identical? alist (seq alist)))
true
user> (identical? (list 1 2 3) (seq (list 1 2 3)))
false

static public ISeq seq(Object coll){
        if(coll instanceof ASeq)
                return (ASeq) coll;
        else if(coll instanceof LazySeq)
                return ((LazySeq) coll).seq();
        else
                return seqFrom(coll);
}

списки - це послідовності, хоча інші речі також є, і не всі послідовності є списками.


Я не маю на увазі обрати невелику точку, це просто можливість вказати на щось корисне. багато хто вже це знатимуть :)
Артур Ульфельт

2
Ви не маєте на увазі classзамість цього class??
qerub

Не впевнений, чи змінився ваш приклад після оновлень clojure (я думаю, що я перебуваю на 1.5), але обидва ваші приклади повертаються clojure.lang.PersistentListдля мене. Я припускаю, що ти мав намір писати classне так class?.
Адріан Муат

Я справді! Я це виправлю
Артур Ульфельт

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