2012-01-20 53 views
6

有没有用Clojure来写这个代码更简单的方法:这似乎更糟Clojure交换!原子离队

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

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

替代的方式来写的那样。

反正我有一个使用原子排队了很多,使用前一种方法是让代码真正混乱代码,我希望那里是这样的:

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

或一些类似的机制交换!函数,因为它看起来像你想要经常做的事情(甚至不仅限于排队),我还碰到了其他一些用例,在这些用例中返回不同的值是有用的,例如,旧值被交换出),它不会破坏原子/交换!语义。

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

回答

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

笑我写了一个CAS我第一次遇到这个问题,但认为这是太冗长并没有考虑它的功能分离 - 感觉现在相当愚蠢:) –

+0

请注意,您可能无法区分队列中的nils和空队列中的nils。 “出列前检查”count“不是线程安全的。所以要小心这些陷阱。 – kotarak

+0

是的 - 还记得那部分 - 如果有人关心 - 上面的第一个解决方案可以修改,以测试是否存在顶级密钥 - 这是空的队列信号。 –

3

使用裁判将是一个简单的选择:

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