You don't need an extra element in the vector to distinguish between empty and full. You can store (start, size) instead of (start, end).
On Feb 3, 12:57 pm, Ken Wesson <kwess...@gmail.com> wrote: > On Thu, Feb 3, 2011 at 2:03 PM, Alan <a...@malloys.org> wrote: > > Or use a fixed-size vector as a ring, with start and end "pointers", > > and a couple functions to abstract over the handling thereof. But > > unless you really really care about performance, this is likely to be > > overkill, and Ken's suggestion to just use a vector or a seq and > > accept the O(n) behavior. > > A ring buffer. I considered using one to implement a deque, but > resizing it presented severe technical difficulties so I shelved that > approach. But for a fixed-size one it's perfect. > > Below is a persistent immutable ring buffer implementation using > deftype. It behaves as a vector that accepts lpeek, lpop, and lconj > acting on the left as well as peek, pop, and conj acting on the right > and has a maximum capacity. Adding at either end when it is at > capacity causes it to drop an element at the other end. The assoc, > nth, and so forth functions work, but the index of a given element > changes if elements are dropped or added at the left end; assoc > accepts index -1 which adds at the left end, as well as index count > which adds at the right, an extension of how assoc can be used to grow > a vector at the right. > > Create one with (ringbuffer capacity item item item ...), or just > (ringbuffer capacity) to get an empty one. The empty function produces > an empty ringbuffer with the same capacity as the argument ringbuffer, > so the capacity of (empty (ringbuffer 5 1 2 3 4 5)) will be 5. > > I've tested it somewhat and it seems to work correctly. Let me know if > you use it, and especially if you run into any bugs. > > Implementation note: internally there is a vector with a size one > greater than capacity (otherwise, start=end wouldn't distinguish an > empty from a full one!) and cap is one greater than capacity (it's the > modulus used for all the pointer arithmetic). The various "mutating" > methods ensure that removed objects are replaced with nil in the > vector (in particular, the "gap" between the last and first element is > always nil) so that things lost from the ring buffer may be promptly > eligible for garbage collection if nowhere else referenced. > > (defprotocol LPop > (lpeek [this]) > (lpop [this]) > (lconj [this object])) > > (defn +mod [a b m] > (mod (+ a b) m)) > > (defn incmod [a m] > (+mod a 1 m)) > > (defn -mod [a b m] > (mod (- a b) m)) > > (defn decmod [a m] > (-mod a 1 m)) > > (deftype RingBuffer [content cap start end] > clojure.lang.IObj > (withMeta [this m] (RingBuffer. (with-meta content m) cap start end)) > (meta [this] (meta content)) > clojure.lang.IPersistentVector > (cons [this object] > (let [e (incmod end cap) > s (if (== start e) (incmod start cap) start)] > (RingBuffer. > (assoc (assoc content end object) e nil) > cap s e))) > (count [this] (-mod (+ end cap) start cap)) > (empty [this] (RingBuffer. (vec (repeat cap nil)) cap 0 0)) > (equiv [this other] (= (seq this) other)) > (seq [this] > (when-not (== end start) > (seq > (if (> end start) > (subvec content start end) > (lazy-cat (subvec content start) (subvec content 0 end)))))) > (peek [this] > (when-not (== start end) > (nth content (decmod end cap)))) > (pop [this] > (if (== start end) > (throw (IllegalStateException. "Can't pop empty ringbuffer")) > (let [e (decmod end cap)] > (RingBuffer. > (assoc content e nil) > cap start e)))) > (containsKey [this object] > (and > (integer? object) > (>= object 0) > (< object (count this)))) > (assoc [this key val] > (if (integer? key) > (if (= key -1) > (lconj this val) > (if (or (< key 0) (> key (count this))) > (throw (IndexOutOfBoundsException.)) > (let [n (+mod key start cap)] > (if (= n end) > (conj this val) > (RingBuffer. (assoc content n val) cap start end))))) > (throw (IllegalArgumentException. "key must be an ingeger")))) > (entryAt [this key] > (if (.containsKey this key) > (first {key (content (+mod key start cap))}))) > (valAt [this key] > (.valAt this key nil)) > (valAt [this key not-found] > (if (.containsKey this key) > (content (+mod key start cap)) > not-found)) > (nth [this key] > (if (.containsKey this key) > (.valAt this key) > (throw (IndexOutOfBoundsException.)))) > (nth [this key not-found] > (.valAt this key not-found)) > (rseq [this] > (if (> end start) > (rseq (subvec content start end)) > (lazy-cat (rseq (subvec content 0 end)) (rseq (subvec content > start))))) > LPop > (lpeek [this] > (when-not (== start end) > (nth content start))) > (lpop [this] > (if (== start end) > (throw (IllegalStateException. "Can't pop empty ringbuffer")) > (RingBuffer. > (assoc content start nil) > cap (incmod start cap) end))) > (lconj [this object] > (let [s (decmod start cap) > ss (decmod s cap) > e (if (== s end) ss end)] > (RingBuffer. > (assoc (assoc content ss nil) s object) > cap s e))) > clojure.lang.IFn > (invoke [this] > (throw (IllegalArgumentException. > "Wrong number of args (0) passed to: RingBuffer"))) > (invoke [this key] > (.nth this key)) > (invoke [this key not-found] > (.nth this key not-found)) > (invoke [this _ _ _] > (throw (IllegalArgumentException. > "Wrong number of args (3) passed to: RingBuffer"))) > (invoke [this _ _ _ _] > (throw (IllegalArgumentException. > "Wrong number of args (4) passed to: RingBuffer"))) > (invoke [this _ _ _ _ _] > (throw (IllegalArgumentException. > "Wrong number of args (5) passed to: RingBuffer")))) > > (defn ringbuffer [capacity & things] > (assert (> capacity 0)) > (let [content (vec (take capacity things)) > end (count content) > cap (inc capacity) > nils (repeat (- cap end) nil)] > (RingBuffer. (apply conj content nils) cap 0 end))) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en