2017-07-07 3 views
1

Nehmen wir an, ich muss einen einfachen Zähler machen, und ich will Zähler jedes Mal erhöht werden, wenn ich diese Funktion aufrufen, aber hier ist eine unangenehme Sache: Definierte 'Zähler' ist nicht lokal und ich kann leicht seinen Wert ändern ein weiterer Raum, der die Kapselung bricht.Gibt es irgendeine Methode zur lokalen "Defonce"? (Clojure)

Viele sagen, es wird korrekt sein, wenn ich "private" Meta-Tag platzieren. Die Funktion sieht dann so aus:

(defn next [] 
    (defonce ^:private counter (atom 0)) 
    (println @counter) 
    (reset! counter (inc @counter))) 

Aber ich habe immer noch Zugriff auf 'Counter' aus einem anderen Raum.
Gibt es eine Möglichkeit, diese Kapselung zu implementieren oder nur auf Vereinbarungsebene?

+0

Sind Sie sicher, dass Sie Zugriff auf private Atom von einem anderen ns haben? – mishadoff

+0

@mishadoff [Ja.] (Https://github.com/bbatsov/clojure-style-guide/blob/cb0be3a21c234fbb5bd152e3d67ffbf104140077/README.md#access-private-var) –

Antwort

4

Hier ist, wie Sie Ihre next Funktion schreiben sollen:

(def ^{:arglists '([])} next 
    (let [counter (atom 0)] 
    #(let [after (swap! counter inc) 
      before (dec after)] 
     (println before) 
     after))) 

Dies ist das gleiche wie die in Ihrer Frage ist, mit der Ausnahme, dass es Thread-sicher ist und vollständig kapselt das counter Atom.

+0

Perfekt! Aber ... '^ {: arglists '([])}' - wofür ist das? – errfrom

+0

@errrfrom Es ist nicht wirklich notwendig; Probieren Sie '(clojure.repl/doc next)' mit und ohne es aus und Sie können entscheiden, ob Sie es aufnehmen möchten. –

1

Privat gut funktioniert, sollten Sie nicht den Zugriff von anderen Namespace haben

user> (ns a) 
nil 
a> (defonce ^:private counter (atom 0)) 
#'a/counter 
a> (defn next [] 
    (println @counter) 
    (swap! counter inc)) 
#'a/next 
a> (next) 
0 
1 
a> (next) 
1 
2 
a> (next) 
2 
3 
a> (next) 
3 
4 
a> (ns b) 
nil 
b> (require 'a) 
nil 
b> (a/next) 
4 
5 
b> (a/next) 
5 
6 
b> a/counter 
CompilerException java.lang.IllegalStateException: var: a/counter is not public 
b> (ns a) 
nil 
a> a/counter 
#object[clojure.lang.Atom 0x1ec64eb6 {:status :ready, :val 6}] 

Auch einige kleinere Probleme:

  1. definieren counter auf der obersten Ebene von ns, nicht innerhalb der Funktion, die beide haben den gleichen Effekt, aber oberste Ebene ist klarer
  2. Änderung reset! zu (swap! counter inc), wird es Thread sicher sein
+0

Es gab ein kleines Missverständnis: Ich will Counter sein nur in dieser Funktion ohne Zugriff von der obersten Ebene als lokale Bindung "lass". Ist es möglich? – errfrom

+0

@errrfrom, ich glaube nicht, dass Sie lokale veränderbare innerhalb einer Funktion ohne Zugriff von außen verbergen können. Verwenden Sie stattdessen defrecord oder deltype (ähnlich wie in Java ein Counter-Objekt) – mishadoff

+0

Ich überarbeitete meinen Code und entschied, dass ich den Teil davon in ein separates Modul legen sollte, wo die Definition auf der obersten Ebene keine Probleme verursachen würde, also im Grunde die Das Problem ist gelöst. Danke =) – errfrom

Verwandte Themen