Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure swap! atom dequeuing

Tags:

clojure

Is there a simpler way to write this code in Clojure :

(def queue (atom {:top nil :queue PersistentQueue/EMPTY}))
(swap! queue #(hash-map :top nil :queue (conj (:queue %) "foo")))
(let [{:keys [top]} (swap! queue
                        #(hash-map 
                           :top (peek (:queue %)) 
                           :queue (pop (:queue %))))]
  (println top))

alternative way to write it would be :

(def queue (atom PersistentQueue/EMPTY))
(swap! queue conj "foo")
(let [top (atom nil)]
  (swap! queue 
         (fn [queue]
           (reset! top (peek queue))
           (pop queue)))
  (println @top))

That seems even worse.

Anyway I have a code which uses atoms for queuing a lot and using the former approach is making the code really confusing, I would expect there to be something like :

(swap! queue (fn [queue] (AtomSwapResult. atom-value return-value))

or some similar mechanism in the swap! function since it seems like the kind of thing you would want to do often (not even limited to queuing, I've hit several other use cases where it would be useful to return a different value, for eg. the old value that was swapped out) and it doesn't break the atom/swap! semantics.

Is there a way to do this in Clojure ?

like image 540
Rafael Munitić Avatar asked Jan 20 '12 07:01

Rafael Munitić


3 Answers

(defn dequeue!
  [queue]
  (loop []
    (let [q     @queue
          value (peek q)
          nq    (pop q)]
      (if (compare-and-set! queue q nq)
        value
        (recur)))))

(def queue (atom clojure.lang.PersistentQueue/EMPTY))
(swap! queue conj :foo)
(swap! queue conj :bar)
(seq @queue)
(dequeue! queue)
(seq @queue)
like image 196
kotarak Avatar answered Oct 30 '22 13:10

kotarak


Using ref would be a simpler option:

(defn dequeue!
  "Given a ref of PersistentQueue, pop the queue and change the queue"
  [queue-ref]
  (dosync
    (let [val (peek @queue-ref)]
      (alter queue-ref pop)
      val)))

(let [q (ref clojure.lang.PersistentQueue/EMPTY)]
           (dosync (alter q conj 1 2 3)
                   (alter q conj 5))
           (fu/dequeue! q)
           => 1
           (seq @q)
           => (2 3 4 5))
like image 44
hzhao Avatar answered Oct 30 '22 13:10

hzhao


Devil's advocate, don't write this in clojure - just use the high quality Java queues that are already available.

(import java.util.concurrent.LinkedBlockingQueue)

(def queue (LinkedBlockingQueue.))

(defn dequeue! [q]
  (.take q))

(defn enqueue! [q v]
  (.put q v))
like image 36
Ethan McCue Avatar answered Oct 30 '22 13:10

Ethan McCue