example defun and variables lisp common-lisp assign variable-declaration

variables - defun - function in lisp



setq y defvar en Lisp (4)

DEFVAR establece una nueva variable. SETQ asigna a una variable.

La mayoría de las implementaciones de Lisp que he utilizado emitirán una advertencia si SETQ a una variable que aún no existe.

Veo que Practical Common Lisp usa (defvar *db* nil) para configurar una variable global . ¿No está bien usar setq para el mismo propósito?

¿Cuáles son las ventajas / desventajas de usar defvar vs. setq ?


defvar introduce una variable dinámica mientras que setq se usa para asignar un valor a una variable dinámica o léxica. El valor de una variable dinámica se busca en el entorno que llama a la función, mientras que el valor de una variable léxica se busca en el entorno donde se definió la función. El siguiente ejemplo aclarará la diferencia:

;; dynamic variable sample > (defvar *x* 100) *X* > (defun fx () *x*) FX > (fx) 100 > (let ((*x* 500)) (fx)) ;; gets the value of *x* from the dynamic scope. 500 > (fx) ;; *x* now refers to the global binding. 100 ;; example of using a lexical variable > (let ((y 200)) (let ((fy (lambda () (format t "~a~%" y)))) (funcall fy) ;; => 200 (let ((y 500)) (funcall fy) ;; => 200, the value of lexically bound y (setq y 500) ;; => y in the current environment is modified (funcall fy)) ;; => 200, the value of lexically bound y, which was ;; unaffected by setq (setq y 500) => ;; value of the original y is modified. (funcall fy))) ;; => 500, the new value of y in fy''s defining environment.

Las variables dinámicas son útiles para pasar un valor predeterminado. Por ejemplo, podemos vincular la variable dinámica *out* a la salida estándar, de modo que se convierta en la salida predeterminada de todas las funciones io. Para anular este comportamiento, simplemente presentamos un enlace local:

> (defun my-print (s) (format *out* "~a~%" s)) MY-PRINT > (my-print "hello") hello > (let ((*out* some-stream)) (my-print " cruel ")) ;; goes to some-stream > (my-print " world.") world

Un uso común de variables léxicas es definir cierres, emular objetos con estado. En el primer ejemplo, la variable y en el entorno de enlace de fy convirtió efectivamente en el estado privado de esa función.

defvar asignará un valor a una variable solo si no está asignado. Por lo tanto, la siguiente redefinición de *x* no cambiará el enlace original:

> (defvar *x* 400) *X* > *x* 100

Podemos asignar un nuevo valor a *x* usando setq :

> (setq *x* 400) 400 > *x* 400 > (fx) 400 > (let ((*x* 500)) (fx)) ;; setq changed the binding of *x*, but ;; its dynamic property still remains. 500 > (fx) 400


defvar y defparameter ambos introducen variables globales. Como observa Ken, setq asigna a una variable.

Además, defvar no destruirá algo previamente defvar -ed. Seibel dice más adelante en el libro (Capítulo 6): "Prácticamente hablando, debe usar DEFVAR para definir variables que contendrán datos que le gustaría conservar incluso si hiciera un cambio en el código fuente que usa la variable".

http://www.gigamonkeys.com/book/variables.html

Por ejemplo, si tiene un *db* global para la base de datos en el capítulo de Base de datos simple:

(defvar *db* nil)

... y empiezas a jugar con él en el REPL - agregando, eliminando cosas, etc. - pero luego haces un cambio al archivo fuente que contiene ese formulario defvar, recargando ese archivo no se borrará *db* y todos los cambios es posible que haya hecho ... Creo que setq hará, como lo hará defparameter . Un Lisper más experimentado, por favor corrígeme si me equivoco.


Hay varias formas de introducir variables.

DEFVAR y DEFPARAMETER introducen variables dinámicas globales . DEFVAR opcionalmente lo establece a algún valor, a menos que ya esté definido. DEFPARAMETER establece siempre en el valor proporcionado. SETQ no introduce una variable.

(defparameter *number-of-processes* 10) (defvar *world* (make-world)) ; the world is made only once.

Tenga en cuenta que probablemente nunca desee DEFVAR variables con nombres como x , y , stream , limit , ... ¿Por qué? Debido a que estas variables serían declaradas especiales y es difícil deshacer eso. La declaración especial es global y todos los demás usos de la variable utilizarán enlace dinámico.

MALO:

(defvar x 10) ; global special variable X, naming convention violated (defvar y 20) ; global special variable Y, naming convention violated (defun foo () (+ x y)) ; refers to special variables X and y (defun bar (x y) ; OOPS!! X and Y are special variables ; even though they are parameters of a function! (+ (foo) x y)) (bar 5 7) ; -> 24

MEJOR: ¡Siempre marque las variables especiales con * en sus nombres!

(defvar *x* 10) ; global special variable *X* (defvar *y* 20) ; global special variable *Y* (defun foo () (+ *x* *y*)) ; refers to special variables X and y (defun bar (x y) ; Yep! X and Y are lexical variables (+ (foo) x y)) (bar 5 7) ; -> 42

Las variables locales se introducen con DEFUN , LAMBDA , LET , MULTIPLE-VALUE-BIND y muchas otras.

(defun foo (i-am-a-local-variable) (print i-am-a-local-variable)) (let ((i-am-also-a-local-variable ''hehe)) (print i-am-also-a-local-variable))

Ahora, de forma predeterminada, las variables locales en las dos formas anteriores son léxicas, a menos que se declaren ESPECIALES . Entonces serían variables dinámicas.

A continuación, también hay varias formas de establecer una variable a nuevos valores. SET , SETQ , SETF y otros. SETQ y SETF pueden establecer variables tanto léxicas como especiales (dinámicas).

Se requiere código portátil para establecer variables que ya están declaradas. El efecto exacto de establecer una variable no declarada no está definido por el estándar.

Entonces, si sabes lo que hace tu implementación de Common Lisp, puedes usar

(setq world (make-new-world))

en Read-Eval-Print-Loop en el toplevel. Pero no lo use en su código, ya que el efecto no es portátil. Por SETQ general, SETQ establecerá la variable. Pero algunas implementaciones también pueden declarar la variable SPECIAL cuando no la conoce (CMU Common Lisp lo hace de manera predeterminada). Eso casi siempre no es lo que uno querría. Úselo para uso casual si sabe lo que hace, pero no para el código.

Igual que aquí:

(defun make-shiny-new-world () (setq world (make-world ''shiny)))

En primer lugar, tales variables se deben escribir como *world* (con los caracteres * rodean), para dejar en claro que es una variable especial global. En segundo lugar, debería haber sido declarado con DEFVAR o DEFPARAMETER antes.

Un compilador de Lisp típico se quejará de que la variable anterior no está declarada. Como las variables léxicas globales no existen en Common Lisp, el compilador debe generar código para una búsqueda dinámica. Algún compilador dice, está bien, suponemos que se trata de una búsqueda dinámica, vamos a declarar que es especial , ya que eso es lo que asumimos de todos modos.