2012-03-28 20 views
10

Estoy tratando de entender algún comportamiento que he notado en Clojure.Clojure let permite enlaces múltiples con el mismo nombre

Es posible crear un let vinculante con el mismo nombre de la unión repitió varias veces:

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

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

entiendo que vamos se evalúan las consolidaciones, y todo esto sobre todo tiene sentido.

Mi comprensión de los documentos es que "Los locales creados con let no son variables. Una vez creados, sus valores nunca cambian!"

¿La sintaxis anterior realmente cambia el valor de los enlaces?

Parece que debería generar un error.

Como una especie de nota al margen:

Es interesante que puede dar salida a lo anterior como JS con clojurescript:

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

Aquí podemos ver que los valores son todas las variables realmente distintas, lo que apunta a lo está sucediendo bajo las sábanas, pero algunas aclaraciones serían muy útiles.

Respuesta

22

(let [a 1, a 2] a) es funcionalmente equivalente a (let [a 1] (let [a 2] a)), que puede ser más fácil de entender. En este último caso, es relativamente fácil darse cuenta de que no está "modificando" el valor de a, sino que introduce una nueva variable no relacionada llamada a con un valor diferente. Puede ver el efecto de esto con algo como (let [a 1] (let [a 2] (println a)) a) - imprime 2, y luego devuelve 1, porque el a externo nunca se cambia, solo se oculta temporalmente. (let [a 1, a 2] a) simplemente está introduciendo un valor llamado a que de inmediato queda fuera del alcance. Por supuesto, el a externo está disponible hasta que el a interno tenga un valor, por lo que puede hacer algo como (let [a 1, a (inc a)] a).

8

let en clojure se comporta como let* de Common Lisp, es decir, permite enlaces posteriores para usar antes. Junto con la reenlace, esto puede ser útil, p. cuando es necesario eliminar algunas capas de datos de una manera limpia:

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

Y por supuesto, esto no es un error. Como habrás notado, estas vinculaciones afectan internamente a diferentes variables. Esto es esencialmente la inmutabilidad de las variables léxicas clojure. Debido a estas variables léxicas, la reenlace tiene una semántica limpia (el último enlace "gana"), y no hay ninguna razón para rechazarla.

6

Otras respuestas han señalado correctamente que la sintaxis de let crea efectivamente nuevas vinculaciones para a que ocultan el enlace anterior.

Un punto adicional interesante a destacar es que esto puede ser muy útil para la optimización del código de Clojure cuando se sabe que un valor tendrá un tipo específico, por ejemplo:

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

Dentro del bloque let, d será ser lanzado y luego utilizado como un doble primitivo que puede acelerar sustancialmente muchas operaciones matemáticas.

Cuestiones relacionadas