2016-06-14 8 views
4

Mit einer Karte in Java können Sie schreiben map.computeIfAbsent(key, f), die überprüft, ob der Schlüssel bereits in der Karte vorhanden ist, und wenn nicht f auf den Schlüssel ruft, um einen Wert zu generieren, und gibt diesen in der Karte ein. Sie haben auch map.putIfAbsent(key, value), die nur den Wert in die Karte einfügt, wenn der Schlüssel nicht bereits in der Karte vorhanden ist.Was entspricht Java computeIfAbsent oder PutIfAbsent in Clojure?

Ignoriert man die Tatsache, dass die Java-Methoden auch den Wert zurückgeben und stattdessen die neue Map zurückgegeben werden soll, was wäre der entsprechende Code in Clojure?

Das Beste, was ich mit so weit kommen habe, ist meine eigenen mit so etwas wie

(defn compute-if-absent [map key f] 
    (if (key map) 
    map 
    (assoc map key (f key)))) 

zu rollen Gibt es eine Alternative meinen eigenen zu rollen?

Antwort

4

Clojure-Karten sind unveränderbar, so dass Sie immer eine neue Karte mit aktualisierten Inhalten zurückgeben - somit sind Ihre Funktionen immer Thread-sicher. Es bedeutet auch, dass Sie keine globale Variable haben können, die Ihre Karte enthält und sie mutiert.

Ihre Implementierung von compute-if-absent ist fast korrekt. Es wird für falsche Schlüssel fehlschlagen, zum Beispiel {false 1}. Sie benötigen if Zustand und contains? statt key verwenden ändern:

(defn compute-if-absent [m k f] 
    (if (contains? m k) 
    m 
    (assoc m key (f k)))) 

Wenn Sie das gleiche Verhalten haben müssen als ConcurrentMap Sie sie nur mit Hilfe von Java-Interop in Clojure verwenden können.

+0

Ich nehme an, Sie bedeuten? "Enthält m-Taste" –

+0

Ja, ich habe es behoben. –

+0

Ich habe dies als die richtige Antwort für die Frage markiert, wie ich es gesagt habe, auch wenn ich jetzt denke, dass ich eigentlich gemeint habe, was z7sg beantwortet - ich habe zumindest dieser Antwort auch eine Stimme gegeben. –

2

Auch einfachere Lösung wäre merge zu verwenden, für put-if-absent:

(merge {key val} m) 

Obwohl das Endergebnis das gleiche für compute-if-absent sein würde, der folgende Code würde natürlich (f key) unabhängig vom Inhalt der m berechnen:

(merge {key (f key)} m) 
+0

glaube nicht, dass es einfacher ist als Piotrs (aber kürzer): zuerst sollten Sie immer die Parameter 'Reihenfolge für 'merge' betrachten, zweitens: Sie rufen jedes Mal das' f' auf, auch wenn der Schlüssel vorhanden ist 'm', wenn also die Wertberechnung schwer ist, ist dies ein enormer Leistungsabfall (und wenn der Wert mit einigen Nebeneffekten berechnet wird, ist er sogar fehleranfällig) – leetwinski

+0

Es ist einfacher für' put-wenn-abwesend' aber nicht für die Rechenversion, die ich in der Antwort erwähnt habe. Ich würde die Verwendung eines "f" mit Nebeneffekten hier fehleranfällig betrachten, nicht die Verwendung von 'merge'. –

+0

wieder, ich sage nicht, 'merge' ist schlecht, ich sage nur, dass es schlecht ist, die Funktion' f' * sowohl * im Falle von Präsenz * als auch * Abwesenheit eines Wertes in einer Karte aufzurufen, anstatt sie aufzurufen nur wenn es kein val gibt.Ich sage auch nicht, dass Nebenwirkungen gut sind, eher sage ich, dass sie nicht unmöglich sind (wie wenn die Wertgenerierung von Elementen aus der Warteschlange abhängt, würden viele Werte aus der Warteschlange "verloren" sein (abgerufen, aber nicht) Wenn diese "f" -Funktion die Logging-Funktion aktiviert hat, würde es Ihnen wahrscheinlich Spaß machen, das resultierende Log mit der ganzen Menge unerwünschter Datensätze zu analysieren. – leetwinski