是否有更简单的方法在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中做到这一点?
答案 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))