2014-11-10 10 views
7

Die meisten Atomoperatoren geben den vorherigen Wert vor dem Austausch zurück, z. B. std::atomic::fetch_add in C++. Es ist normal, atomisches int als globale steigende ID beginnend mit 0 zu verwenden. Warum gibt Clojures Atom den Wert zurück, der eingetauscht wird?Warum tauscht Clojures Atom aus? den neuen Wert zurückgeben?

Gibt es eine bessere Möglichkeit, einen Null-basierten Zähler in Clojure zu erstellen?

+2

'(def global-counter (Atom -1))' und loswerden 'dec' Aufruf? – hsestupin

+0

@hsestupin es funktioniert nicht für unsigned Int, die sowieso nicht in Java existiert :) – woodings

+0

ok, schau dir meine Antwort unten an. AtomicInteger ist schneller als Clojure-Atome und sieht mehr idiomatisch und passender für Ihren Fall aus. – hsestupin

Antwort

2

swap! ermöglicht es Ihnen, eine beliebige Funktion auf das Atom anwenden, und Sie wissen nicht im Voraus, was das Ergebnis wird Sein. Wenn swap! Ihnen den Postwert nicht gab, wäre es weniger nützlich; Sie müssten eine Transaktion erstellen, um sie zu entfernen, wenn Sie das Ergebnis wissen wollten (oder jemand könnte es aktualisieren, bevor Sie es veraltet haben).

+0

'swap!' Erlaubt keine Nebenwirkungen von 'fn'. Wenn es einfach den vorherigen Wert zurückgibt, kann ich '(fn old-val)' für den neuen Wert tun. Ich weiß, dass es weniger optimal ist, das 'fn' hier zweimal auszuführen. – woodings

+1

Siehe auch diese Frage: http://stackoverflow.com/questions/15441638/alternate-version-of-swap-also-returning-swapped-out-value –

+1

Dies ist einfach nicht wahr. Wenn 'swap!' Den Vortransaktionswert zurückliefert, können Sie das Ergebnis auch durch Aufrufen der Transaktion in der Anwendung erstellen, wie in meinem Beispiel gezeigt. Es wäre für jemanden, der an dem Wert nach der Transaktion interessiert ist, noch nützlicher, da "Tauschen!" Für jemanden nützlich ist, der an dem Wert vor Transaktion interessiert ist, weil manchmal der Vortransaktionswert nicht neu erstellt werden kann. Transaktionswert Derefing ist auch für die Frage nicht relevant. –

2

Sie möchten vielleicht java.util.concurrent.atomic.AtomicInteger zum Zwecke der Schaffung globaler Erhöhung ID verwenden:

(def global-counter (AtomicInteger. 0)) 
(defn next! [] (.getAndIncrement global-counter)) 
2

Gegenfrage: Wissen Sie, warum std::atomic::fetch_add den Vortransaktionswert zurückgibt? (Tue ich nicht.)

Aufruf swap! führt eine Transaktion durch und gibt ihr Ergebnis zurück. Wenn in einem gleichzeitigen Szenario der Vortransaktionswert zurückgegeben wird, besteht die einzige deterministische Möglichkeit zum Abrufen des Transaktionsergebnisses darin, die in Transaktion befindliche Anwendung zu wiederholen, z. G.

(def pre-tx (std-swap! global-counter inc)) 
(def tx-result (inc pre-tx)) 

Sicherlich könnte ein umgekehrtes Beispiel für den Wert vor der Transaktion nachgeholt werden. In den meisten Fällen (und auch in Ihrem Beispielfall - siehe unten) ist das tx-Ergebnis der Wert, der für die weitere Referenz relevant ist. Deshalb wurde swap! entworfen, um es direkt zurückzugeben. Für unterschiedliche Anforderungen ist eine ref geeignet (Sofern nicht eine atom Voraussetzung ist, müssen Sie in diesem Fall eine eigene Spin-Schleife mit compare-and-set! erstellen).

In Ihrem Beispiel sollten next!1 beim ersten Anruf zurückzukehren, und es gibt keinen Grund zu dec es solange Zählen der gegebenen Beispiel ist. Das Zählen beginnt immer mit einem: Wenn Sie eins zählen, ist Ihre Gesamtzahl 1. Wenn next! jemals zurückgegeben wird 0 (imaginär) last! würde -1, eine ungültige Gesamtzahl zurückgeben.

+1

Wer hat das abgelehnt? Warum? –

+1

Ich stimme zu. Gute Antwort. Upvoted zurück auf Null. – Mars

Verwandte Themen