2012-04-24 21 views
18

La sección API Cheatsheet en Listas parece indicar que '() es un constructor de lista, al igual que (list), pero he descubierto que en la práctica no son exactamente iguales. Por ejemplo, teniendo en cuenta:¿Cuál es la diferencia entre '() y (lista) en Clojure?

(def foo "a") 
(def bar "b") 
(def zip "c") 

La siguiente declaración:

(apply str '(foo bar zip)) 

produce la salida "foobarzip", que yo no esperaría.

Pero el supuestamente equivalentes:

(apply str (list foo bar zip)) 

produce "abc", como me esperaba.

¿Qué está pasando aquí? Si hay una "taquigrafía" para una lista en Clojure (como {} para los mapas y [] para los vectores), ¿qué es?

+0

No hay ninguna abreviatura para una lista porque una lista es de importancia secundaria para Clojure. –

Respuesta

31

En lisps, ' (como quote) cita sus argumentos, es decir, los conserva exactamente como están escritos en su formulario s-exp, incluido el no evaluar nada dentro.

Para decirlo de otra manera '(foo bar zip) crea una lista que contiene los símbolos foo, bar, zip; mientras que (list foo bar zip) crea una lista que contiene los valores de foo, bar, zip. En el primer caso, str convertirá los símbolos en cadenas y luego las concatenará.

Como una demostración de esto:

=> (def foo "a") 
=> (type (first '(foo))) 
clojure.lang.Symbol 
=> (type (first (list foo))) 
java.lang.String 
+2

@Jonathan En mi experiencia, la mayoría de las veces es más conveniente usar un vector en lugar de una lista citada. Además de requerir menos pulsaciones de teclas, un vector se distingue más fácilmente como una lista no evaluada que una lista citada. – user100464

+0

¿Podría comentar también los casos vacíos, ''()' y '(list)'? ¿Hay alguna diferencia entre ellos? – Lii

+0

@Lii no, creo que son lo mismo. – huon

10

La diferencia es que, como se puede ver, la sintaxis literal '() es citado. Esto significa que los símbolos en el interior no se evalúan. Para utilizar literales de la lista, mientras que la evaluación de los elementos que se pueden explotar el syntax-quote lector macro:

user=> (apply str `(~foo ~bar ~zip)) 
"abc" 
3

Cuando se escribe:

(def foo "a") 
(def bar "b") 
(def zip "c") 

que haya definido tres símbolos: foo, barzip y asociados con los valores: "a", "b" y "c".

La asociación se almacena dentro de la tabla namsepace, por lo que si el uso de la réplica, el espacio de nombres sería user por defecto, por lo que la tabla de espacio de nombres de usuario podría contener ahora:

{foo 
#'user/foo 
bar 
#'user/bar, 
zip 
#'user/zip} 

se puede ver la tabla de espacio de nombres haciendo: (ns-interns *ns*)

Ahora bien, si escribe: (foo bar zip) dentro de Clojure, el lector podrá hacerlo de cuatro formas diferentes. Deberá especificar al lector de qué manera debe leerse. Ahí es donde entran en juego `, ' y list.

Caso de ningún indicador:

(foo bar zip) 

Cuando simplemente por escrito sin ningún indicador, el lector interpretará esto como una S-expresión y la interpretará foo como una correlación de símbolos a una función, con bar y zip como asignación de símbolos a los valores que se pasarán a la función foo.

En nuestro caso, se producirá una excepción:

java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn 

Esto es porque no se definió una función foo, foo se asoció con "a", que es una cadena, no un polipéptido IFN (una función Clojure).

Si se define la función foo, habría llamado foo pasando como argumento "b" y "c".

Caso de list:

(list foo bar zip) 

Cuando se utiliza el símbolo de la lista, el lector es en realidad interpretando esta la misma manera que el caso no es indicativa. Es decir, está buscando un símbolo list que se asigna a una función que tomará los valores asociados mapeados a foo, bar y zip como argumentos. La función list está predefinida por Clojure dentro del espacio de nombres clojure.core; devuelve una lista de sus argumentos.

Así que cuando el lector busca list, se encuentra la función clojure.core, entonces busca el símbolo foo, y encuentra que se asigna a "a", y así sucesivamente. Una vez que ha encontrado toda la asignación para los símbolos, llama al list y le pasa los valores asociados de foo bar zip, que serían "a" "b" "c". Entonces (list foo bar zip) es lo mismo que (list "a" "b" "c").

Caso de ':

'(foo bar zip) 

La cita ' le dice al lector que la forma que sigue debe ser leída como es. En nuestro caso, foo, bar y zip son símbolos, y (foo bar zip) es una lista de símbolos. Entonces el lector interpretará esto como una lista de símbolos.

Es por eso que cuando ejecuta (apply str '(foo bar zip)) va a llamar al str 'foo 'bar 'zip que le dará foobarzip. Es decir, convertirá cada símbolo en la lista de símbolos a una representación de Cadena, y luego los concatenará en una Cadena.

Tomando el formulario como está, pasa como argumento una lista de símbolos, sin evaluar los símbolos, es decir, sin buscar con qué están asociados. Si ejecutó (eval '(foo bar zip)), pasará una lista de símbolos al eval, y eval evaluará los símbolos a valores y devolverá una lista de los valores a los que se asignan los símbolos. Entonces, puede pensar en la cita ' como pasar el código como código.

Caso de `:

`(foo bar zip) 

Ésta es un poco más complicado. El lector verá la comilla inversa ` y resolverá los símbolos dentro de la lista de símbolos recursivamente para obtener una lista de símbolos totalmente calificados.

Básicamente, cuando el lector busca símbolos dentro de la tabla de símbolos, lo hace desde la tabla de símbolos del espacio de nombres actual. Un símbolo completamente calificado, es un símbolo que incluye la información del espacio de nombres. Por lo tanto, al ejecutar `(foo bar zip), el lector reemplazará esos símbolos con los completamente calificados, convirtiéndolo en (user.foo user.bar user.zip).

Es posible decirle al lector que evalúe algunos de los elementos en la lista, mientras cambia otros a símbolos totalmente calificados. Para ello, el prefijo de los símbolos que desea evaluados con ~ como en:

`(foo ~bar zip) 

le dará

(clojure.foo "b" clojure.zip) 

Efectivamente, la comilla inversa ` es mucho más similar a la cita ' en que lo hace no evalúa, sino que simplemente devuelve el código, excepto que manipula el código para que sea devuelto un poco por los símbolos totalmente calificables dentro de él. Esto tiene implicaciones para las macros, donde a veces es posible que desee una referencia totalmente calificada, para buscar desde otro espacio de nombres, y algunas veces desea flexibilidad al decir, busque este símbolo en el espacio de nombres actual.

Cuestiones relacionadas