2016-07-22 13 views
3

Eine Liste von Spielern als ein Satz definiert:entfernen Element aus einem Satz nur, wenn Element in es

(def players (atom #{})) 

Funktion, die Spieler entfernt sollte auf verschiedene HTTP-Codes zurückgeben basierend, ob Element in der Menge war oder nicht :

(defn remove-player [player-name] 
    (if (contains? @players player-name) 
    (do (swap! players disj player-name) 
     (status (response "") 200)) 
    (status (response "") 404))) 

Das Problem mit diesem Code ist, dass es eine Anfrage mehr 200 auf gleichzeitige Anforderungen zurückkehren könnte, auch wenn sie eigentlich nur das Element entfernt.

Ich denke, ich muss sowohl contains? als auch disj atomar ausführen. Muss ich explizit sperren oder gibt es einen besseren Weg?

+1

Sie können sich bewegen, wenn Bedingung '' 'swap!' '' Funktion machen beide '' 'enthält?' 'Und' '' disj'' nennt atomare. –

Antwort

3

Die swap! selbst ist atomare Operation, so dass Sie innerhalb der Berechnungsfunktion des Swaps sicher sein können, dass der erste Parameter (der aktuelle Wert des Atoms) konsistent ist. Persönlich würde ich eine Hilfsfunktion machen das wie folgt aus:

(defn remove-existent [value set-a] 
    (let [existed (atom false)] 
    (swap! set-a 
      #(if (contains? % value) 
       (do (reset! existed true) 
        (disj % value)) 
       (do (reset! existed false) 
        %)) 
    @existed)) 

wie Sie sehen können, Lambda-Ausdruck enthält sowohl existency Überprüfung und Entfernung.

user> (def players (atom #{:user1 :user2})) 
#'user/players 

user> (remove-existent :user100 players) 
false 

user> (remove-existent :user1 players) 
true 

user> @players 
#{:user2} 

Update

Inspiriert von @ clojuremostly ist ausgezeichnet Metadaten Ansatz, können Sie es viel besser machen:

(defn remove-existent [value set-a] 
    (-> (swap! set-a #(with-meta (disj % value) 
         {:existed (contains? % value)})) 
     meta 
     :existed)) 
+0

danke, ich konnte nicht herausfinden, wie man sowohl das neue Set als auch eine Flagge zurückgibt, Sie haben es mit atom gelöst. – damluar

+0

habe meinen Code aktualisiert, um Atom auf false zurückzusetzen, wenn der Wert nicht vorhanden war. – leetwinski

+0

müssen wir es tatsächlich zurücksetzen? 'existierte' ist lokal, oder? – damluar

2

Sie können nur etwas mehr Logik zum Swapping-Funktion hinzu:

(for [el-rem [:valid-el :not-there]] 
    (let [a (atom #{:valid-el :another-one}) 
     disj-res 
     (swap! a 
       (fn [a] 
       (with-meta (disj a el-rem) 
          {:before-count (count a)})))] 
    [disj-res 
    "removed:" 
    el-rem 
    (not (== (:before-count (meta disj-res)) 
       (count disj-res)))])) 

Sie vergleichen dann th e Zählung des Rückgabewerts und die Anzahl in den Metadaten. Wenn es sich unterscheidet, hat disj ein Element entfernt. Wenn nicht, war das Element nicht vorhanden.

+0

meinst du, ich sollte die aktuelle Anzahl vor dem Tausch speichern und dann vergleichen? hat es nicht das gleiche Problem? Der Unterschied könnte ein beliebiger Wert sein (+100, -50, ...) – damluar

+0

Nein, auch wenn sich das Atom unter dir ändert, ist der Rückgabewert von 'swap!' immer noch der Schnappschuss zu dem Zeitpunkt, an dem du "disj".Es ist also absolut sicher und sollte 2x schneller sein, da du 'contains?' Nicht darüber laufen lassen musst. – ClojureMostly

+0

ziemlich ordentlich Trick. Ich mag ! :) – blushrt