Когда в Clojure следует использовать вектор поверх списка и наоборот?

Я читал, что векторы - это не последовательности, а списки. Я не уверен, в чем причина использования одного над другим. Кажется, что чаще всего используются векторы, но есть ли для этого причина?


person Rayne    schedule 18.07.2009    source источник
comment
Связанный stackoverflow.com/questions/ 6928327 /   -  person Duncan McGregor    schedule 14.04.2013


Ответы (5)


И снова, похоже, я ответил на свой вопрос, проявив нетерпение и задав его в #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
person Rayne    schedule 18.07.2009
comment
Пока вы находитесь на freenode, переходите на темную сторону и присоединяйтесь к #stackoverflow! :-П - person Chris Jester-Young; 18.07.2009
comment
Я вообще-то там бездельничал. Я переключил клиентов IRC и никогда не думал добавлять #stackoverflow в свой список автосоединения. - person Rayne; 18.07.2009
comment
Я новичок в Лиспе, но мне было интересно, нарушают ли векторы, карты и множества каким-то образом идею о том, что весь код взаимозаменяем с данными? Или это лишь одна из тех вещей, которые делают Clojure практичным Lisp? (ИЛИ, вы можете оценить вектор?) - person Rob Grant; 25.02.2014
comment
Это совершенно бесполезный фрагмент чата. Генерация кода, генерирующая обратную связь - ›точно означает ?? У меня действительно возникают трудности с этим вопросом, потому что в моей книге лень + декларативный стиль = гораздо лучшая производительность, и тем не менее векторы предлагаются повсюду в Clojure, что меня полностью сбивает с толку. - person Jimmy Hoffa; 26.02.2014
comment
@JimmyHoffa Как я понимаю: Генерация кода = внутри макроса (потому что большая часть кода - это вызовы функций, то есть списки); генерация назад на передний план = построение последовательности путем добавления. - person omiel; 04.04.2014
comment
@RobertGrant Код Clojure также состоит из векторов и карт. Например, список аргументов функции (гудение) является вектором. - person omiel; 04.04.2014
comment
Я согласен, что это не очень хороший ответ. Хороший ответ должен содержать большую нотацию O таких вещей, как доступ к элементу по индексу, получение count, добавление элемента и изменение структуры данных по индексу. - person user983716; 30.12.2016

Если вы много занимались программированием на Java и знакомы со средой сбора данных Java, подумайте о списках, таких как LinkedList, и векторах, таких как ArrayList. Таким образом, вы можете выбирать контейнеры таким же образом.

Для дальнейшего пояснения: если вы намереваетесь добавлять элементы по отдельности в начале или в конце последовательности, связанный список намного лучше, чем вектор, потому что элементы не нужно каждый раз перемещать. Однако, если вы хотите часто обращаться к определенным элементам (не в начале или в конце списка) (т. Е. Произвольный доступ), вам следует использовать вектор.

Кстати, векторы легко превратить в seqs.

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)
person Chris Jester-Young    schedule 18.07.2009
comment
Векторы не являются последовательностями, но они последовательны. (Источник: сам Рич на #clojure на freenode.) Кроме того, я вообще не знаю Java, но Рич только что ответил на мой вопрос. - person Rayne; 18.07.2009
comment
Я отредактирую свой пост, чтобы сказать, что векторы могут быть преобразованы в seqs с помощью функции seq. :-) - person Chris Jester-Young; 18.07.2009
comment
Выберите свой ответ, потому что он действительно ответил на вопрос, и мне действительно не нравится выбирать свои собственные ответы как правильные. Не кажется правильным. Спасибо. :) - person Rayne; 18.07.2009
comment
Двухсторонняя очередь лучше связанного списка в случае добавления первого и последнего. LL довольно ужасны: P - person boxed; 24.12.2013
comment
@boxed Да, но deques не встроены в Clojure, в отличие от списков и векторов. - person Chris Jester-Young; 24.12.2013
comment
@boxed В Java LinkedList ‹E› реализует интерфейс Deque ‹E›. Afaik, использующий связанные списки, является стандартным для создания двухсторонних дек. - person Lucas Hoepner; 19.01.2015
comment
@LucasHoepner может быть стандартным. К тому же почти всегда поступать неправильно. ОСОБЕННО в java, когда сами содержащиеся объекты не встроены в узел (например, в C ++ или что-то еще). Связанные списки в этом случае занимают больше памяти, больше ЦП / медленнее. Довольно просто реализовать двухстороннюю очередь поверх vector / ArrayList, так что она не в стандартной библиотеке clojure кажется неуместной. - person boxed; 20.01.2015
comment
@boxed Невозможно реализовать двухстороннюю очередь поверх вектора или ArrayList без эффективной реализации ArrayDeque самостоятельно. - person Chris Jester-Young; 20.01.2015

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

person Svante    schedule 18.07.2009
comment
Технически связанные списки имеют время доступа O (1) ... если вы обращаетесь только к переднему или заднему элементу. :-P Однако векторы имеют произвольный доступ O (1). :-) - person Chris Jester-Young; 18.07.2009
comment
(Связанный список, как описано выше, относится к двусвязным спискам. Односвязные списки имеют доступ O (1) только к переднему элементу. :-P) - person Chris Jester-Young; 18.07.2009
comment
Поскольку кто-то только что погрузился в Clojure, это НАМНОГО лучший ответ, чем два других с большим количеством голосов. Два других мне ничего полезного не говорят. - person keithjgrant; 19.06.2014
comment
@ ChrisJester-Young Односвязный список может поддерживать доступ O (1) к задней части, если он хранит ссылку на задний элемент, вот так. - person Gill Bates; 11.05.2015

Когда использовать вектор:

  • Производительность индексированного доступа - вы получаете ~ O (1) стоимость для индексированного доступа по сравнению с O (n) для списков
  • Добавление - с конгидом ~ O (1)
  • Удобные обозначения - мне легче набирать и читать [1 2 3], чем '(1 2 3) для буквального списка в обстоятельствах, когда любой из них будет работать.

Когда использовать список:

  • Если вы хотите получить к нему доступ как к последовательности (поскольку списки напрямую поддерживают seq без необходимости выделять новые объекты)
  • Преобразование - добавление в начало списка с помощью минусов или, желательно, константы O (1)
person mikera    schedule 24.11.2011
comment
Даже при добавлении / удалении с обоих концов список - довольно ужасный выбор. Дека намного лучше (в процессоре и особенно в памяти). Попробуйте github.com/pjstadig/deque-clojure - person boxed; 24.12.2013
comment
Re: ~O(1), для тех, кому это объяснение стоимости может быть полезно - stackoverflow.com / questions / 200384 / constant-amortized-time - person Merlyn Morgan-Graham; 29.03.2016

just a quick side note:

"I read that Vectors are not seqs, but Lists are." 

последовательности являются более универсальными, чем списки или векторы (или карты или наборы).
К сожалению, REPL печатает списки и последовательности одинаково, потому что это действительно делает вид, будто списки являются последовательностями, даже если они разные. функция (seq) будет создавать последовательность из множества разных вещей, включая списки, и затем вы можете передать эту последовательность любой из множества функций, которые делают отличные вещи с помощью seqs.

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 есть ярлык, который возвращает свой аргумент, если это уже seq:

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);
}

списки - это последовательности, хотя есть и другие вещи, и не все последовательности являются списками.

person Arthur Ulfeldt    schedule 20.07.2009
comment
Я не хочу заострять внимание на мелочах, это просто возможность указать на что-нибудь полезное. многие уже знают это :) - person Arthur Ulfeldt; 21.07.2009
comment
Разве вы не имеете в виду class вместо class?? - person qerub; 04.08.2012
comment
Не уверен, изменился ли ваш пример после обновлений clojure (я думаю, что я на 1.5), но оба ваших примера возвращают clojure.lang.PersistentList для меня. Полагаю, вы хотели написать class, а не class?. - person Adrian Mouat; 25.10.2013
comment
Я действительно это сделал! Я исправлю это - person Arthur Ulfeldt; 26.10.2013
comment
Все еще немного запутался; поскольку class возвращает один и тот же PersistentList для обоих упомянутых вами выражений, это означает, что последовательности и списки действительно одно и то же? - person johnbakers; 03.01.2014
comment
есть сокращение в секундах, которое возвращает аргумент, если это уже последовательность. поэтому seq возвращает свой аргумент в приведенном выше примере. Я вставлю код для этого в ответ. - person Arthur Ulfeldt; 03.01.2014