2012-03-28 20 views
10

Sto cercando di capire alcuni comportamenti che ho notato in Clojure.Clojure let consente più associazioni con lo stesso nome

E 'possibile creare un let vincolante con lo stesso nome-binding ripetuto più volte:

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

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

Capisco che consentono attacchi vengono valutati, e questo rende tutti per lo più senso.

La mia comprensione dai documenti è che "i locali creati con let non sono variabili. Una volta creati i loro valori non cambiano mai!"

La sintassi precedente modifica effettivamente il valore dei binding?

Sembra che dovrebbe generare un errore.

come una sorta di lato nota:

Interessante è possibile l'uscita di cui sopra come JS con ClojureScript:

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

Qui possiamo vedere che i valori sono tutte variabili realtà distinte, che indica di quanto sta accadendo sotto le coperte, ma qualche chiarimento sarebbe molto utile.

risposta

22

(let [a 1, a 2] a) è funzionalmente equivalente a (let [a 1] (let [a 2] a)), che può essere più facile da capire. In quest'ultimo caso, è relativamente facile rendersi conto di non "modificare" il valore di a, ma di introdurre una nuova variabile non correlata denominata a con un valore diverso. È possibile vedere l'effetto di questo con qualcosa come (let [a 1] (let [a 2] (println a)) a) - stampa 2, e quindi restituisce 1, perché l'esterno a non viene mai modificato, solo temporaneamente nascosto. (let [a 1, a 2] a) sta semplicemente introducendo un valore denominato a che esce immediatamente dall'ambito. Naturalmente, l'esterno a è disponibile fino a quando l'interno a ha un valore, quindi è possibile fare qualcosa come (let [a 1, a (inc a)] a).

8

let in clojure si comporta come let* da Common Lisp, ovvero consente l'utilizzo di associazioni successive in precedenza. In combinazione con il rebinding, può essere utile per es. quando è necessario rimuovere alcuni strati di dati in modo pulito:

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

E naturalmente questo non è un errore. Come avrai notato, questi binding influenzano internamente diverse variabili. Questo essenzialmente è l'immutabilità delle variabili lessicali del clojure. A causa di queste variabili lessicali, il rebinding ha una semantica pulita (l'ultima vincita "vince"), e non vi è alcun motivo per impedirlo.

6

Altre risposte hanno correttamente notato che la sintassi let crea effettivamente nuovi collegamenti per un che nasconde il vecchio legame.

Un interessante punto supplementare da notare è che questo può essere molto utile per l'ottimizzazione del codice Clojure quando si sa che un valore avrà un tipo specifico, ad esempio:

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

All'interno del blocco let, d sarà essere lanciato poi usato come un doppio primitivo che può sostanzialmente accelerare molte operazioni matematiche.

Problemi correlati