Me estoy enseñando un poco más de elisp y he encontrado el siguiente problema:
Si quiero restablecer una variable de lista, no se actualizará después de la primera evaluación. Aquí hay un código de ejemplo:
(defun initilize ()
(setq example '(3)))
(defun modify ()
(initilize)
(message "%S" example)
(setcar example 2))
; M-x eval-buffer RET
(modify) ; message --> (3)
(modify) ; message --> (2)
(modify) ; message --> (2)
Estoy interesado en dos cosas. La primera es aprender más sobre lo que está sucediendo "bajo el capó", entonces, ¿por qué funciona la primera vez y falla en llamadas posteriores?
La segunda pregunta, más práctica, es cómo reiniciar la lista correctamente o ¿hay otra forma común de hacer algo así?
Una solución alternativa que encontré es usar una lista citada y evaluar el contenido de esta manera:
(setq example `(,3))
list
quote
mutability
clemera
fuente
fuente
'(some list)
a estareq
a'(some list)
- nunca .Hay general hay garantía de que en Lisp código que visiblemente cita una lista de declaraciones de nueva estructura de la lista cada vez. En algunas implementaciones de Lisp podría, o podría ser, algunas veces. En otros, nunca lo hace. Su código no debería depender de ningún comportamiento de la implementación. Si quieres nueva estructura de la lista, el usolist
ocons
o equivalente.example
nunca se ha declarado como una variable, por lo quesetq
debe actuar como si declarara una nueva variable, pero más tarde, cuando llameinitialize
nuevamente, se creará una nueva variable, mientrasmodify
recuerda la anterior ... en cualquier caso, este no es un comportamiento esperado, sin embargo, el uso desetq
algo que no se ha introducido anteriormente como una variable también podría estar indefinido.'(3)
se trata como un valor literal, así que una vez(setcar '(3) 2)
, cada vez que haces(defvar foo '(3))
o(let ((foo '(3)))
etc. Es probable que obtener un valor defoo
igual a'(2)
. Digo "probable" porque este comportamiento no está garantizado, es un tipo de optimización que el intérprete hace cuando se siente, algo conocido como eliminación de subexpresiones constantes (un caso particular de). Entonces, lo que escribió abo-abo no es exactamente la razón. Es más como modificar un literal de cadena en C (que generalmente genera una advertencia).Respuestas:
Quizás esto aclare algo de la confusión:
Su función
initilize
no inicializa la variableexample
. Lo establece en una celda de contra en particular: la misma celda de contras cada vez que se llama. La primera vez queinitilize
se llama, sesetq
asignaexample
a una nueva celda de contras, que es el resultado de la evaluación'(3)
. Llamadas posteriores parainitilize
simplemente reasignarexample
a la misma celda de contras.Dado que
initilize
solo reasigna la misma celda de contrasexample
,modify
solo establece el auto de esa misma celda de contras2
cada vez que se llama.Para inicializar una lista, use
list
ocons
(o un sexp entre comillas equivalente, como`(,(+ 2 1))
o`(,3)
). Por ejemplo, use(list 3)
.La clave para comprender esto es saber que una celda contratada entre comillas se evalúa solo una vez, y luego se devuelve la misma celda contra. Esta no es necesariamente la forma en que se comportan todos los Lisps, pero es cómo se comporta Emacs Lisp.
De manera más general, el comportamiento de evaluar un objeto mutable entre comillas depende de la implementación, si no del lenguaje. En Common Lisp, por ejemplo, estoy bastante seguro de que no hay nada en la definición (especificación) del lenguaje que defina el comportamiento a este respecto; se deja a la implementación.
Resumen: No espere que '(alguna lista) sea igual a' (alguna lista), nunca. Por lo general, no hay garantía en Lisp de que el código que cita visiblemente una lista devuelva una nueva estructura de lista cada vez. En algunas implementaciones de Lisp podría, o podría ser, algunas veces. En otros, nunca lo hace. Su código no debería depender de ningún comportamiento de la implementación. Si quieres nueva estructura de la lista, el uso
list
ocons
o equivalente.fuente
Puede usar
(setq example (list 3))
para evitar este error.Lo que pasa es
init
cesionarios un objeto que inicialmente contiene(3)
aexample
. Establece el valor del objeto solo una vez. Posteriormente, modifica este valor.Aquí está su ejemplo en C ++, si lo entiende mejor:
fuente
initilize
función y llamomodify
nuevamente, se mostrará(3)
nuevamente solo porque reevalúe la función.(3)
es un objeto temporal del que forma parteinit
.init
El cuerpo se ajustaexample
a la dirección de ese objeto temporal, no toca su valor.