Clojure交换!原子队列出局

时间:2012-01-20 07:59:56

标签: clojure

是否有更简单的方法在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))

写它的替代方法是:

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

这似乎更糟。

无论如何,我有一个使用原子进行大量排队的代码,使用前一种方法会让代码真的很混乱,我希望有类似的东西:

(swap! queue (fn [queue] (AtomSwapResult. atom-value return-value))
交换中的一些类似机制!函数,因为它似乎是你想要经常做的事情(甚至不限于排队,我已经遇到了几个其他用例,其中返回一个不同的值是有用的,例如,交换的旧值out)并且它不会打破原子/交换!语义。

有没有办法在Clojure中做到这一点?

3 个答案:

答案 0 :(得分:16)

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

答案 1 :(得分:3)

使用ref将是一个更简单的选项:

(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)]
           (dosync (alter q conj 1 2 3)
                   (alter q conj 5))
           (fu/dequeue! q)
           => 1
           (seq @q)
           => (2 3 4 5))

答案 2 :(得分:0)

魔鬼的拥护者,不要用clojure编写它-只需使用已经可用的高质量Java队列即可。

(import java.util.concurrent.LinkedBlockingQueue)

(def queue (LinkedBlockingQueue.))

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

(defn enqueue! [q v]
  (.put q v))