【问题标题】:Clojure swap! atom dequeuingClojure 交换!原子出队
【发布时间】:2012-01-20 07:59:56
【问题描述】:

有没有更简单的方法可以在 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 中做到这一点?

【问题讨论】:

    标签: clojure


    【解决方案1】:
    (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)
    

    【讨论】:

    • 大声笑,我第一次遇到问题时写了一个 CAS,但认为它太冗长并且没有考虑将它分离到一个函数中 - 现在感觉很愚蠢:)
    • 请注意,您可能无法区分队列中的 nil 和空队列中的 nil。在dequeue! 之前检查count 不是线程安全的。所以要小心陷阱。
    • 是的 - 也记住那部分 - 如果有人关心 - 上面的第一个解决方案可以修改以测试是否存在顶键 - 这就是空队列信号。
    • 漂亮而简单。很好的答案
    • Justed 想补充一点,dequeue! 可以简化,我认为使用swap-vals! 也可以加快速度。 (defn dequeue! [queue] (ffirst (swap-vals! queue pop)))
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      魔鬼的拥护者,不要在 clojure 中写这个 - 只需使用已经可用的高质量 Java 队列。

      (import java.util.concurrent.LinkedBlockingQueue)
      
      (def queue (LinkedBlockingQueue.))
      
      (defn dequeue! [q]
        (.take q))
      
      (defn enqueue! [q v]
        (.put q v))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多