2012-03-28 12 views
10

Ich versuche, etwas Verhalten zu verstehen, das ich in Clojure bemerkt habe.Clojure Let erlaubt mehrere Bindungen mit dem gleichen Namen

Es ist möglich, ein let-Bindung mit dem gleichen Bindungs-Namen mehrere Male wiederholt zu erstellen:

(let [a 1 a 2 a b] a) 
; (= a 2) 

(let [a 1 a 2 a 3] a) 
; (= a 3) 

ich, dass Bindungen verstehen läßt ausgewertet werden, und das macht alles meist Sinn.

Mein Verständnis von der Dokumentation ist, dass "Locals erstellt mit Let sind keine Variablen. Einmal erstellt ihre Werte nie ändern!"

Ändert die obige Syntax tatsächlich den Wert der Bindungen?

Das fühlt sich an, als ob es einen Fehler verursachen sollte.

Als eine Art Randnotiz:

Interessanter können Sie die Ausgabe der oben als JS mit clojurescript:

var a__36584 = 1, b__36585 = 2, a__36586 = b__36585; 
var a__30671 = 1, a__30672 = 2, a__30673 = 3; 

Hier können wir sehen, dass die Werte alle tatsächlich verschiedene Variablen sind, welche Punkte auf, was geschieht unter der Decke, aber eine Klärung wäre sehr hilfreich.

Antwort

22

(let [a 1, a 2] a) ist funktionell äquivalent zu (let [a 1] (let [a 2] a)), die einfacher zu verstehen sein kann. Im letzteren Fall ist es relativ einfach zu erkennen, dass Sie den Wert a nicht "ändern", sondern eine neue, nicht verwandte Variable namens a mit einem anderen Wert einführen. Sie können den Effekt mit etwas wie (let [a 1] (let [a 2] (println a)) a) sehen - es druckt 2 und gibt dann 1 zurück, weil die äußere a nie geändert wird, nur vorübergehend ausgeblendet. (let [a 1, a 2] a) führt einfach einen Wert namens a ein, der sofort den Gültigkeitsbereich verlässt. Natürlich ist die äußere a verfügbar, bis die innere a einen Wert hat, so dass Sie etwas wie (let [a 1, a (inc a)] a) tun können.

8

let in clojure verhält sich wie let* von Common Lisp, das heißt, es ermöglicht spätere Bindungen früher zu verwenden. In Verbindung mit der Wiederverbindung kann dies z.B. wenn brauchen Sie ein paar Schichten von Daten in einer sauberen Art und Weise zu entfernen:

(let [a some-vector, a (first a), a (:key a)] a) 

Und natürlich dies kein Fehler ist. Wie Sie bemerkt haben, beeinflussen diese Bindungen intern verschiedene Variablen. Dies ist im Wesentlichen die Unveränderlichkeit der lexikalischen Variablen. Aufgrund dieser lexikalischen Variablen haben Rebinds saubere Semantik (die letzte Bindung "gewinnt"), und es gibt keinen Grund, sie zu verbieten.

6

Andere Antworten haben richtig angemerkt, dass die let-Syntax effektiv neue Bindungen für a erzeugt, die die alte Bindung verbergen.

Ein interessanter zusätzlicher Punkt zu beachten ist, dass diese für die Optimierung von Clojure Code kann sehr nützlich sein, wenn Sie wissen, dass ein Wert eines bestimmten Typs haben, zB:

(let [d (double d)] 
    ......) 

Im let Block, d wird gegossen werden, dann als primitives Doppel verwendet werden, was viele mathematische Operationen wesentlich beschleunigen kann.

Verwandte Themen