Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fixed length stack structure in Clojure

Tags:

queue

clojure

What's the best data structure for a fixed length stack (I originally called it a queue, but what I want is a stack) where items are added to the front, and every time an item is added to the front an item is removed from the end? Various lengths of subvectors will be accessed from the front also. I was using vectors, now thinking about clojure.lang.PersistentQueue and finger trees.

edit, to clarify, something like:

> (def q (queue [1 2 3 4]))
[1 2 3 4]
> (push q 9 8 7)
[7 8 9 1]
> (peek (push q 9 8 7))
7

edit2: thanks for all your answers so far, this has turned into an exercise in going back to basics and reading Joy of Clojure, learning for instance that subvec of subvec retains a reference to the first subvec's vector, while something like (vec (cons x (subvec... would if used repeatedly accrue references to all intermediate subvecs. In light of this, how about this implementation of push for a vector-based queue ?:

(defn push [v i n] (if (>= (count v) n) (conj (subvec v 1 n) i) (conj v i) ) )

then the resulting vector could be accessed via rseq which I believe is fast with vectors (due to its use of index-offset?)

like image 903
Hendekagon Avatar asked Nov 12 '12 23:11

Hendekagon


2 Answers

Have a look at Amalloy's ring buffer at https://github.com/amalloy/ring-buffer

like image 194
DanLebrero Avatar answered Oct 03 '22 07:10

DanLebrero


IMO you can use just a list:

(defn create-queue [len]
  (atom (repeat len nil)))

(defn push [queue new-items]
  (let [len (count @queue)
        len2 (count new-items)]
    (if (>= len2 len)
      (let [res (concat (take-last (- len2 len) new-items)
                        @queue)]
        (reset! queue (take len new-items))
        res)
      (let [res (take-last len2 @queue)]
        (reset! queue (concat new-items
                              (drop-last len2 @queue)))
        res))))

test:

(def q (create-queue 4))

(take 4 @q)
-> (nil nil nil nil)
(push q [1 2 3])
-> (nil nil nil)
(take 4 @q)
-> (1 2 3 nil)
(push q [4 5])
-> (3 nil)
(take 4 @q)
-> (4 5 1 2)
(push q [6 7 8 9])
-> (4 5 1 2)
(take 4 @q)
-> (6 7 8 9)
(push q [10 11 12 13 15 16])
-> (15 16 6 7 8 9)
(take 4 @q)
-> (10 11 12 13)
like image 26
mobyte Avatar answered Oct 03 '22 07:10

mobyte