Ответ 1
Векторы не предназначены для добавления. У вас есть только O (n) preend:
user=> (into [:foo] [:bar :baz])
[:foo :bar :baz]
Что вы хотите, скорее всего, пальцем.
Превращение в список легко:
user=> (conj '(:bar :baz) :foo)
(:foo :bar :baz)
Добавление к вектору легко:
user=> (conj [:bar :baz] :foo)
[:bar :baz :foo]
Как я (идиоматически) приступаю к вектору, возвращая вектор? Это не работает, поскольку он возвращает seq, а не вектор:
user=> (cons :foo [:bar :baz])
(:foo :bar :baz)
Это уродливое (IMVHO):
user=> (apply vector (cons :foo [:bar :baz]))
[:foo :bar :baz]
Примечание. Я просто хочу создать структуру данных, которую я могу добавить и добавить. Добавление к большим спискам должно иметь большое ограничение производительности, поэтому я думал о векторах.
Векторы не предназначены для добавления. У вас есть только O (n) preend:
user=> (into [:foo] [:bar :baz])
[:foo :bar :baz]
Что вы хотите, скорее всего, пальцем.
Я знаю, что этот вопрос старый, но никто ничего не говорил о различиях списки, и, поскольку вы говорите, что действительно хотите что-то, что вы можете добавить и добавьте с, это звучит, как разницы могут помочь вам. Они не кажутся популярными в Clojure, но они ОЧЕНЬ легки для реализации и намного менее сложных, чем пальцевые деревья, поэтому я сделал крошечная библиотека списков различий, только сейчас (и даже проверила ее). Эти объединить в O (1) время (добавить или добавить). Преобразование разницы список обратно в список должен стоить вам O (n), что является хорошим компромиссом, если вы делаете много конкатенации. Если вы не делаете много конкатенация, то просто придерживаться списков, не так ли?:)
Вот функции в этой крошечной библиотеке:
dl: Разностный список - это фактически функция, которая объединяет свои собственные содержимое с аргументом и возвращает результирующий список. Каждый раз вы создаете список различий, вы создаете небольшую функцию, которая действует как структура данных.
dlempty:. Поскольку список различий просто конкатцирует его содержимое с аргумент, пустой список различий - это то же самое, что и идентификатор функция.
undl: Из-за того, что делают списки различий, вы можете конвертировать список различий в обычный список, просто вызвав его с помощью nil, так что это функция не нужна; это просто для удобства.
dlcons: переносит элемент в начало списка - не полностью необходимо, но consing - достаточно обычная операция, и это просто однострочный (как и все функции, здесь).
dlappend: объединяет два списка различий. Я считаю, что его определение самое интересное - проверьте это!:)
И вот, вот эта крошечная библиотека - 5 однострочных функций, которые дают вам O (1) добавить/добавить структуру данных. Неплохо, а? Ах, красота Лямбды Исчисление...
(defn dl
"Return a difference list for a list"
[l]
(fn [x] (concat l x)))
; Return an empty difference list
(def dlempty identity)
(defn undl
"Return a list for a difference list (just call the difference list with nil)"
[aDl]
(aDl nil))
(defn dlcons
"Cons an item onto a difference list"
[item aDl]
(fn [x] (cons item (aDl x))))
(defn dlappend
"Append two difference lists"
[dl1 dl2]
(fn [x] (dl1 (dl2 x))))
Вы можете увидеть это в действии с этим:
(undl (dlappend (dl '(1 2 3)) (dl '(4 5 6))))
который возвращает:
(1 2 3 4 5 6)
Это также возвращает то же самое:
((dl '(1 2 3)) '(4 5 6))
Получайте удовольствие от списков различий!
Обновление
Вот некоторые определения, которые могут быть труднее понять, но я думаю, что лучше:
(defn dl [& elements] (fn [x] (concat elements x)))
(defn dl-un [l] (l nil))
(defn dl-concat [& lists] (fn [x] ((apply comp lists) x)))
Это позволяет вам сказать что-то вроде этого:
(dl-un (dl-concat (dl 1) (dl 2 3) (dl) (dl 4)))
Что вернет
(1 2 3 4)
Как пользователь optevo сказал в комментариях под ответом пальцев, вы можете использовать clojure/core.rrb-vector lib, который реализует RRB- деревья:
RRB-деревья построены на Clojure PersistentVectors, добавляя логарифмическую конкатенацию времени и нарезку. ClojureScript поддерживается с тем же API, за исключением отсутствия функции
vector-of
.
Я решил опубликовать это как отдельный ответ, потому что я думаю, что эта библиотека заслуживает этого. Он поддерживает ClojureScript и поддерживается Michał Marczyk, который довольно известен в сообществе Clojure за его работу по внедрению различных структур данных.
Я бы предложил использовать удобные функции встроенные в библиотеку Tupelo. Например:
(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]
по сравнению с raw Clojure легко ошибиться:
; Add to the end
(concat [1 2] 3) ;=> IllegalArgumentException
(cons [1 2] 3) ;=> IllegalArgumentException
(conj [1 2] 3) ;=> [1 2 3]
(conj [1 2] 3 4) ;=> [1 2 3 4]
; Add to the beginning
(conj 1 [2 3] ) ;=> ClassCastException
(concat 1 [2 3] ) ;=> IllegalArgumentException
(cons 1 [2 3] ) ;=> (1 2 3)
(cons 1 2 [3 4] ) ;=> ArityException
Если вы не боитесь квазицитирования, это решение на самом деле довольно элегантное (для некоторых определений "элегантный"):
> '[~:foo [email protected][:bar :baz]]
[:foo :bar :baz]
Я на самом деле использую это иногда в реальном коде, так как декларативный синтаксис делает его довольно читабельным ИМХО.