2011-12-29 7 views
12

He empezado a aprender a programar con Emacs Lisp. Estoy tan confundido por la cita de símbolo. Por ejemplo:cuando para citar el símbolo en Emacs Lisp

(progn 
    (setq a '(1 2)) 
    (prin1 a) 
    (add-to-list 'a 3) 
    (prin1 a) 
    (setcar a 4) 
    (prin1 a) 
    (push 5 a) 
    "" 
) 

por qué la función "add-to-list" necesita un símbolo citado como primer argumento, mientras que el "setcar" y la función "push" necesitan ninguna cotización argumento?

Respuesta

18

Aquí hay un diagrama que representa el símbolo a y su valor después de (setq a '(1 2)). Los recuadros son estructuras de datos elementales (símbolos y conses) y las flechas son punteros (donde una parte de los datos hace referencia a otra). (Estoy simplificando un poco.)

symbol      cons    cons 
+-------+----------+  +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+------+ 
      |    ​↑   |  ↑ 
      +-------------+   +-------+ 

La expresión '(1 2) construye los dos conses a la derecha, que conforman una lista de dos elementos. La expresión (setq a '(1 2)) crea el símbolo a si no existe, luego hace que su "ranura variable" (la parte que contiene el valor del símbolo) apunte a la lista recién creada. setq es una macro incorporada, y (setq a '(1 2)) es la abreviatura de (set 'a '(1 2)). El primer argumento de set es el símbolo para modificar y el segundo argumento es el valor para establecer la ranura variable del símbolo.

(add-to-list 'a 3) es equivalente a (set 'a (cons 3 a)) aquí, porque 3 no está en la lista. Esta expresión hace cuatro cosas:

  1. Crea una nueva celda de cons.
  2. Establezca el nuevo campo de coche de la celda de cons en 3.
  3. Establezca el nuevo campo cdr de la celda cons para el valor anterior (y aún actual) de a (es decir, copie el contenido de la ranura variable de a).
  4. Establezca la ranura variable de a en la nueva celda de cons.

Después de esa llamada, las estructuras de datos involucrados mirada como esto:

symbol      cons    cons    cons 
+-------+----------+  +------+--|---+ +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 3 | | | | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+--|---+ +------+------+ 
      |    ​↑   |  ↑   |  ↑ 
      +-------------+   +-------+   +-------+ 

La llamada a setcar no crea ninguna nueva estructura de datos, y no actúa en el símbolo a sino en su valor, que es la célula contras cuya car contiene actualmente 3. Después (setcar a 4), las estructuras de datos el siguiente aspecto:

symbol      cons    cons    cons 
+-------+----------+  +------+--|---+ +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 4 | | | | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+--|---+ +------+------+ 
      |    ​↑   |  ↑   |  ↑ 
      +-------------+   +-------+   +-------+ 

push es una macro; aquí, (push 5 a) es equivalente a (set 'a (cons 5 a)).

setq y push son macros (setq es una “forma especial”, que por lo que nos preocupa aquí significa una macro cuya definición está incorporado en el intérprete y no previsto en Lisp). Las macros reciben sus argumentos sin evaluar y pueden elegir expandirlos o no. set, setcar y add-to-list son funciones que reciben sus argumentos evaluados. La evaluación de un símbolo devuelve los contenidos de su ranura variable, p. después del (setq a '(1 2)) inicial, el valor del símbolo a es la celda de cons cuyo automóvil contiene 1.

Si todavía está confundido, sugiero experimentar con (setq b a) y ver por sí mismo cuál de las expresiones modificar b cuando se actúa sobre a (los que actúan sobre el símbolo a) y cuáles no (los que actuar sobre el valor del símbolo a).

13

Las funciones evalúan sus argumentos antes de la ejecución, así que cite cuando necesite pasar un símbolo real (como puntero a alguna estructura de datos, por ejemplo) y no cite cuando es un valor variable.

add-to-list realiza la mutación en contexto de su primer argumento, por lo que necesita un símbolo entre comillas.

push no es una función, sino una macro; es por eso que es capaz de aceptar argumentos sin comillas sin evaluación. Las formas incorporadas, como setcar, tampoco tienen esa limitación.

+0

¿Puede decir si algo es una macro o una función puramente de su documentación sin mirar la implementación? – Tom

+1

La documentación de @Tom generalmente indica eso inmediatamente en la primera línea (para push - 'push es una macro Lisp en \' cl.el''). –

+0

Sí, pero ¿qué hay de setcar? ¿Cómo sabe por la documentación que no evalúa su argumento? – Tom

8

Las otras respuestas dadas hasta ahora aclarar el uso de quote y la diferencia entre funciones, por un lado, y las macros y formas especiales por otro lado.

Sin embargo, no llegan a otra parte de la pregunta: ¿por qué es add-to-list como es? ¿Por qué requiere que su primer argumento sea un símbolo? Esa es una pregunta separada de si evalúa el argumento o no. Es la verdadera pregunta detrás del diseño de add-to-list.

Uno podría imaginar que add-to-list evaluó su args, y espera que el valor de la primera arg ser una lista, y luego añade el valor de la segunda arg a esa lista como un elemento y devuelve el resultado (lista nueva o la misma lista). Eso le permitiría hacer (add-to-list foo 'shoe) para agregar el símbolo shoe a la lista que es el valor de foo - digamos (1 2 buckle) -, para dar (1 2 buckle shoe).

El punto es que tal función no sería muy útil. ¿Por qué? Porque el valor de la lista no es necesariamente accesible. La variable foo podría considerarse una forma de acceder a ella, un "identificador" o un "puntero". Pero eso no es cierto de la lista que la función devuelve. Esa lista devuelta puede estar compuesta de una nueva estructura de lista, y normalmente no hay nada (ninguna variable) que apunte a esa lista. La función add-to-list nunca ve el símbolo (variable) foo - no tiene forma de saber que el valor de la lista que recibe como primer argumento está vinculado a foo. Si add-to-list se diseñaron de esa manera, entonces aún tendría que asignar su resultado devuelto a su variable de lista.

IOW, add-to-list evalúa sus argumentos porque es una función, pero eso no explica mucho. Se espera un símbolo como el valor de su primer arg. Y espera que el valor de esa variable (símbolo) sea una lista. Agrega el valor de su segundo arg a la lista (posiblemente cambiando la estructura de la lista) y establece el valor de la variable que es el valor de su primer arg a esa lista.

En pocas palabras: Se necesita un símbolo como arg, ya que su trabajo es asignar un nuevo valor a ese símbolo (el nuevo valor es el mismo valor de lista o el mismo valor con el nuevo elemento de la lista Agregado en el frente).

Y sí, otro camino a seguir sería utilizar una macro o una forma especial, como en push. Es la misma idea: push quiere un símbolo como segundo argumento. La diferencia es que push no evalúa sus argumentos por lo que no es necesario citar el símbolo. Pero en ambos casos (en Emacs Lisp) el código necesita obtener un símbolo con el fin de establecer su valor en la lista aumentada.

+0

Creo que es un punto que vale la pena en general, pero no es verdad en el caso de las listas, dado que solo son indicadores de una celda de cons. Por ejemplo, considere '(defun my-add-to-list1 (valor de lista) (setcdr (última lista) (valor cero de cons)) list)' o '(defun my-add-to-list2 (valor de lista) (setcdr) lista (cons (car list) (cdr list))) (lista de valor de setcar lista)) – phils

+1

El punto no era que no se pueda tener una función que agregue un valor a una lista que no sea el valor de un símbolo. El punto es que el punto de 'add-to-list' es agregar un elemento al valor de la lista de una variable. IOW, 'add-to-list' es * todo sobre establecer un valor de variable *. Es *** no *** solo una función para agregar un elemento a una lista (a pesar de su nombre). – Drew

+0

Supongo que la diferencia importante es que al pasar el * símbolo * se obtiene la capacidad de cambiar el valor de ese símbolo * sin afectar los valores de otros símbolos *, mientras que si se pasa la lista evaluada, todos los símbolos que apuntan a ese la lista puede verse afectada. – phils