Frage

OK. Ich habe mit Clojure bastelt und ich laufe ständig in das gleiche Problem. Lassen Sie sich dieses kleine Fragment von Code übernehmen:

(let [x 128]
  (while (> x 1)
    (do
      (println x)
      (def x (/ x 2)))))

Jetzt erwarte ich, wie mit 128 Starten einer Sequenz auszudrucken so:

128
64
32
16
8
4
2

Stattdessen ist es eine Endlosschleife, Druck 128 über und über. Offensichtlich meine beabsichtigte Nebenwirkung nicht funktioniert.

Wie soll ich den Wert von x in einer Schleife wie folgt neu zu definieren? Ich weiß, das kann nicht wie Lisp wird (ich könnte eine anonyme Funktion verwenden, die recurses auf es selbst ist, vielleicht), aber wenn ich herausfinden, nicht, wie variabel einstellen wie diese, ich bin verrückt gehen wird.

Meine andere Vermutung wäre, gesetzt zu verwenden !, aber das gibt „Ungültige Zuordnung Ziel“, da ich nicht in einer verbindlichen Form bin.

Bitte, erleuchte mich, wie dies funktionieren soll.

War es hilfreich?

Lösung

def definiert einen Toplevel var, auch wenn Sie es in einer Funktion oder innere Schleife von Code verwenden. Was Sie in let bekommen sind nicht Vars. Per in der Dokumentation zu let :

  

Die Einheimischen let erstellt sind keine Variablen. Sobald ihre Werte erstellt nie ändern!

Sie brauchen nicht veränderbaren Zustand für Ihr Beispiel hier (Schwerpunkt nicht meint.); Sie loop und recur nutzen könnten.

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

Wenn Sie wollten Lust, Ihnen die explizite loop ganz vermeiden können.

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
  (doseq [x xs] (println x)))

Wenn Sie wirklich wollte wandelbaren Zustand verwenden, ein Atom funktionieren könnte .

(let [x (atom 128)]
  (while (> @x 1)
    (println @x)
    (swap! x #(/ %1 2))))

(Sie brauchen keinen do; while hüllt seinen Körper in einem expliziten für Sie.) Wenn Sie wirklich, wirklich wollte, dies zu tun mit vars etwas schreckliches wie dies zu tun haben würde.

(with-local-vars [x 128]
  (while (> (var-get x) 1)
    (println (var-get x))
    (var-set x (/ (var-get x) 2))))

Das ist aber sehr hässlich und es ist nicht idiomatische Clojure alle an. So verwenden Sie Clojure effektiv sollten Sie versuchen, in Bezug auf die wandelbaren Zustand aufhören zu denken. Es wird auf jeden Fall fahren verrückt versuchen, Clojure Code in einem nicht-funktionalen Stil zu schreiben. Nach einer Weile können Sie es finden eine angenehme Überraschung zu sein, wie selten tatsächlich Sie änderbare Variablen benötigen.

Andere Tipps

Vars (das ist, was Sie bekommen, wenn Sie „def“ etwas) nicht neu zugewiesen werden soll (kann aber sein):

user=> (def k 1)
#'user/k
user=> k
1

Es gibt nichts, was Sie zu tun zu stoppen:

user=> (def k 2)
#'user/k
user=> k
2

Wenn Sie einen Thread-local einstellbaren „Ort“ wollen, können Sie „Bindung“ und „set“:

user=> (def j) ; this var is still unbound (no value)
#'user/j
user=> j
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0)
user=> (binding [j 0] j)
0

So können Sie eine Schleife wie folgt schreiben:

user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil

Aber ich denke, das ist ziemlich unidiomatic ist.

Wenn Sie denken, dass in reinen Funktionen wandelbar lokale Variablen, die wäre eine schöne praktische Funktion, die nicht schadet, weil die Funktion noch rein bleibt, könnten Sie in dieser Mailingliste Diskussion interessiert, wo Reich Hickey seine Gründe erläutert, für sie zu entfernen von der Sprache. Warum nicht wandelbar Einheimischen?

Relevante Teil:

  

Wenn Einheimischen waren Variablen, das heißt wandelbar, dann könnte Verschlüsse schließen über   wandelbarer Zustand und in Anbetracht, dass Verschlüsse (ohne etwas mehr entweichen können   Verbot der gleichen), wäre das Ergebnis fad unsicher. Und Leute   sicherlich tun würde, z.B. Verschluss-basierten Pseudo-Objekte. Das Ergebnis   ein riesiges Loch in Clojure Ansatz wäre.

     

Ohne wandelbar Einheimischen sind die Menschen gezwungen, erneut auftreten zu verwenden, ist ein Funktions   Schleifenkonstrukt. Während dies zunächst seltsam erscheinen mag, ist es genauso   prägnanter als Schleifen mit der Mutation und die sich ergebenden Muster sein kann   an anderer Stelle in Clojure, das heißt wieder auftreten, verringern, verändern, pendeln usw. wiederverwendet   sehr ähnlich sind (logisch). Obwohl ich konnte erkennen und   verhindern mutiert Verschlüsse an der Flucht, habe ich beschlossen, es auf diese Weise zu halten   für Konsistenz. Selbst im kleinsten Zusammenhang nicht-mutierende Schleifen   leichter zu verstehen und zu debuggen als diejenigen mutiert. In jedem Fall Vars   ist für den Einsatz bei Bedarf.

Die Mehrheit der nachfolgenden Beiträge betrifft ein with-local-vars Makro implementiert;)

Sie mehr idiomatisch iterate und take-while stattdessen verwenden könnte,

user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top