Anton Harald
Anton Harald

Reputation: 5944

clojure: pop and push

I'm looking for a sequential data structure which is perfect for the following operation. The lenght of the list remains constant, it will never be longer or shorter than a fixed length.

Omit the first item and add x to the end.

(0 1 2 3 4 5 6 7 8 9)

(pop-and-push "10")

(1 2 3 4 5 6 7 8 9 10)

There is only one other reading-operation that has to be done equally often:

(last coll)

pop-and-push could be implemented like this:

(defn pop-and-push [coll x]
   (concat (pop coll) ["x"]))

(unfortunately this does not work with sequences produced by e.g. range, it just pops when the sequence declared by the literals '(..) is passed.)

but is this optimal?

Upvotes: 1

Views: 1256

Answers (1)

noisesmith
noisesmith

Reputation: 20194

The main issue here (once we change "x" to x) is that concat returns a lazy-seq, and lazy-seqs are invalid args to pop.

user=> (defn pop-and-push [coll x] (concat (pop coll) [x]))
#'user/pop-and-push
user=> (pop-and-push [1 2 3] 4)
(1 2 4)
user=> (pop-and-push *1 5)
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentStack  clojure.lang.RT.pop (RT.java:730)

My naive preference would be to use a vector. This function is easy to implement with subvec.

user=> (defn pop-and-push [v x] (conj (subvec (vec v) 1) x))
#'user/pop-and-push
user=> (pop-and-push [1 2 3] 4)
[2 3 4]
user=> (pop-and-push *1 5)
[3 4 5]

as you can see, this version can actually operate on its own return value

As suggested in the comments, PersistentQueue is made for this situation:

user=> (defn pop-and-push [v x] (conj (pop v) x))
#'user/pop-and-push
user=> (pop-and-push (into clojure.lang.PersistentQueue/EMPTY [1 2 3]) 4)
#object[clojure.lang.PersistentQueue 0x50313382 "clojure.lang.PersistentQueue@7c42"]
user=> (into [] *1)
[2 3 4]
user=> (pop-and-push *2 5)
#object[clojure.lang.PersistentQueue 0x4bd31064 "clojure.lang.PersistentQueue@8023"]
user=> (into [] *1)
[3 4 5]

The PersistentQueue data structure, though less convenient to use in some ways, is actually optimized for this usage.

Upvotes: 4

Related Questions