2012-01-20 26 views
6

Có một cách đơn giản hơn để viết mã này trong Clojure:Trao đổi Clojure! nguyên tử dequeuing

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

cách khác để viết nó sẽ là:

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

Đó dường như còn tồi tệ hơn.

Dù sao tôi có một mã số trong đó sử dụng các nguyên tử cho xếp hàng rất nhiều và sử dụng phương pháp cũ đang thực hiện các mã thực sự bối rối, tôi mong chờ có được một cái gì đó như:

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

hoặc một số cơ chế tương tự trong trao đổi! chức năng vì nó có vẻ giống như loại thứ bạn muốn làm thường xuyên (thậm chí không giới hạn trong việc xếp hàng, tôi đã nhấn một số trường hợp sử dụng khác, nơi nó sẽ hữu ích để trả về một giá trị khác, ví dụ như giá trị cũ được đổi chỗ ra ngoài) và nó không phá vỡ nguyên tử/trao đổi! ngữ nghĩa.

Có cách nào để thực hiện việc này trong Clojure không?

Trả lời

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

lol tôi đã viết CAS một lần đầu tiên tôi gặp phải vấn đề nhưng nghĩ rằng nó là quá dài dòng và không xem xét tách nó trong một chức năng - cảm giác khá ngu ngốc ngay bây giờ :) –

+0

Lưu ý, rằng bạn có thể không thể phân biệt giữa các nils từ hàng đợi và nils từ một hàng đợi rỗng. Việc kiểm tra với 'count' trước' dequeue! 'Không an toàn cho thread. Vì vậy hãy cẩn thận những cạm bẫy. – kotarak

+0

Yup - hãy nhớ rằng một phần quá - nếu có ai quan tâm - giải pháp đầu tiên ở trên có thể được sửa đổi để kiểm tra xem khóa trên có hiện diện không - và đó là tín hiệu hàng đợi trống. –

3

Sử dụng ref sẽ là một lựa chọn đơn giản hơn:

(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)) 
Các vấn đề liên quan