What is the idiomatic way of adding to a vector in Clojure?

Listing is easy:

user=> (conj '(:bar :baz) :foo) (:foo :bar :baz) 

Adding to a vector is easy:

 user=> (conj [:bar :baz] :foo) [:bar :baz :foo] 

How do I (idiomatically) proceed with a vector, returning a vector? This does not work as it returns seq, not a vector:

 user=> (cons :foo [:bar :baz]) (:foo :bar :baz) 

This is ugly (IMVHO):

 user=> (apply vector (cons :foo [:bar :baz])) [:foo :bar :baz] 

Note. I just want to create a data structure that I can add and add. Adding to large lists should have a big performance limitation, so I was thinking about vectors.

+54
append vector clojure prepend
Nov 04 2018-10-10T00:
source share
5 answers

Vectors are not intended to be added. You only have O (n) preend:

 user=> (into [:foo] [:bar :baz]) [:foo :bar :baz] 

What you want is most likely a finger .

+69
Nov 04 '10 at 10:40
source share

I know this question is old, but no one said anything about the differences of the listings, and since you say you really want something that you can add and add with, it sounds like the differences can help you. They don't seem popular in Clojure, but they are VERY easy to implement and much less complex than finger trees, so I made a tiny library of difference lists just now (and even tested it). These combine in O (1) time (add or add). Converting a list difference back to a list should cost you O (n), which is a good compromise if you do a lot of concatenation. If you don't do a lot of concatenation, then just stick to the lists, right? :)

Here are the features in this tiny library:

dl: A difference list is actually a function that combines its own contents with an argument and returns a resulting list. Each time you create a list of differences, you create a small function that acts like a data structure.

dlempty:. Since the list of differences simply concatenates its contents with an argument, an empty list of differences is the same as the identifier of the function.

undl: Because of the difference lists, you can convert a difference list to a regular list simply by calling it with nil, so this function is not needed; It is just for convenience.

dlcons: moving an element to the top of the list is not completely necessary, but consing is a fairly ordinary operation, and it is just single-line (like all functions, here).

dlappend: Combines two lists of differences. I think his definition is the most interesting - check it out! :)

And so, here is this tiny library - 5 single-line functions that give you O (1) add / add data structure. Not bad, huh? Ah, the beauty of Lambda Calculus ...

 (defn dl "Return a difference list for a list" [l] (fn [x] (concat lx))) ; 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)))) 

You can see it in action with this:

 (undl (dlappend (dl '(1 2 3)) (dl '(4 5 6)))) 

which returns:

 (1 2 3 4 5 6) 

This also returns the same:

 ((dl '(1 2 3)) '(4 5 6)) 

Enjoy the lists of differences!

Update

Here are some definitions that may be harder to understand, but I think it’s better:

 (defn dl [& elements] (fn [x] (concat elements x))) (defn dl-un [l] (l nil)) (defn dl-concat [& lists] (fn [x] ((apply comp lists) x))) 

This allows you to say something like this:

 (dl-un (dl-concat (dl 1) (dl 2 3) (dl) (dl 4))) 

What will return

 (1 2 3 4) 
+16
Nov 12
source share

As user optevo said in the comments under the answer of the fingers, you can use clojure / core.rrb-vector lib, which implements RRB trees:

RRB trees are built on Clojure PersistentVectors, adding logarithmic time concatenation and slicing. ClojureScript is supported with the same API, except for the lack of a vector-of function.

I decided to post this as a separate answer because I think this library deserves it. It supports ClojureScript and is supported by Michał Marczyk , who is quite famous in the Clojure community for his work in implementing various data structures.

+2
Oct. 14 '15 at 19:05
source share

I would suggest using the convenient features built into the Tupelo library . For example:

 (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] 

Compared to raw Clojure it’s easy to make a mistake:

 ; 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 
+2
Jul 07 '16 at 19:42
source share

If you are not afraid of quasi-quotation, this solution is actually quite elegant (for some definitions, “elegant”):

 > '[~:foo ~@[:bar :baz]] [:foo :bar :baz] 

I actually use this sometimes in real code, since the declarative syntax makes it pretty readable IMHO.

0
Dec 21 '18 at 19:53
source share



All Articles