2012-01-20 13 views
6

¿Hay una manera simple de escribir este código en Clojure:Clojure swap! átomo de desencola

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

forma alternativa de escribir sería:

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

que parece aún peor.

De todos modos tengo un código que utiliza átomos para poner en cola mucho y utilizando el enfoque anterior es que el código realmente confuso, que sería de esperar que haya algo como:

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

o algún mecanismo similar en el ¡intercambiar! función ya que parece ser el tipo de cosas que le gustaría hacer a menudo (ni siquiera se limita a la cola, he golpeado varios otros casos de uso en los que sería útil devolver un valor diferente, por ejemplo, el valor anterior que se cambió) out) y no rompe el átomo/intercambio. semántica.

¿Hay alguna manera de hacer esto en Clojure?

Respuesta

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 escribí CAS una primera vez me encontré con el problema, pero pensé que era demasiado detallado y no consideró que lo separa de una función - la sensación bastante tonto en este momento :) –

+0

Tenga en cuenta que probablemente no pueda distinguir entre nils de la cola y nils de una cola vacía. ¡Comprobar con 'count' antes de' dequeue! 'No es seguro para subprocesos. Así que ten cuidado con las trampas. – kotarak

+0

Sí, recuerda esa parte también, si a alguien le importa, la primera solución anterior se puede modificar para probar si está presente la tecla superior, y esa es la señal de cola vacía. –

3

Usando ref sería una opción más simple:

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