web-dev-qa-db-fra.com

setq et defvar en lisp

Je vois que le LISP pratique commun utilise (defvar *db* nil) pour configurer une variable globale . N'est-il pas acceptable d'utiliser setq dans le même but?

Quels sont les avantages/inconvénients de l'utilisation de defvar par rapport à setq?

49
prosseek

Il existe plusieurs façons d'introduire des variables.

DEFVAR et DEFPARAMETER introduire global variables dynamiques . DEFVAR lui attribue éventuellement une valeur, sauf si elle est déjà définie. DEFPARAMETER le définit toujours à la valeur fournie. SETQ n'introduit pas de variable.

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

Notez que vous ne voudrez probablement jamais DEFVAR variables avec des noms comme x, y, stream, limit, ... Pourquoi? Parce que ces variables seraient alors déclarées spéciales et il est difficile de les annuler. La déclaration spéciale est globale et toutes les autres utilisations de la variable utiliseraient une liaison dynamique.

MAUVAIS:

(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

MIEUX: Marquez toujours les variables spéciales avec * en leurs noms!

(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

Les variables locales sont introduites avec DEFUN , LAMBDA =, LAISSEZ , MULTIPLE-VALUE-BIND et bien d'autres.

(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))

Maintenant, par défaut, les variables locales dans les deux formes ci-dessus sont lexicales, sauf si elles sont déclarées [~ # ~] spéciale [~ # ~]. Ce seraient alors des variables dynamiques.

Ensuite, il existe également plusieurs formes pour définir une variable sur de nouvelles valeurs. SET =, SETQ , SETF et autres. SETQ et SETF peuvent définir des variables lexicales et spéciales (dynamiques).

Il est nécessaire pour le code portable que l'on définit des variables qui sont déjà déclarées. L'effet exact de la définition d'une variable non déclarée n'est pas défini par la norme.

Donc, si vous savez ce que fait votre implémentation LISP commune, vous pouvez utiliser

(setq world (make-new-world))

dans le Read-Eval-Print-Loop au niveau supérieur. Mais ne l'utilisez pas dans votre code, car l'effet n'est pas portable. Généralement, SETQ définit la variable. Mais certaines implémentations peuvent également déclarer la variable [~ # ~] spéciale [~ # ~] lorsqu'elle ne la connaît pas (CMU Common LISP le fait par défaut). Ce n'est presque pas toujours ce que l'on voudrait. Utilisez-le pour un usage occasionnel si vous savez ce que vous faites, mais pas pour le code.

Pareil ici:

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

Tout d'abord, ces variables doivent être écrites comme *world* (avec l'entourage * caractères), pour indiquer clairement qu'il s'agit d'une variable spéciale globale. Deuxièmement, il aurait dû être déclaré avec DEFVAR ou DEFPARAMETER auparavant.

Un compilateur LISP typique se plaindra que la variable ci-dessus n'est pas déclarée. Comme les variables lexicales globales n'existent pas dans Common LISP, le compilateur doit générer du code pour une recherche dynamique. Certains compilateurs disent alors, d'accord, nous supposons qu'il s'agit d'une recherche dynamique, déclarons que c'est spécial - puisque c'est ce que nous supposons de toute façon.

45
Rainer Joswig

defvar introduit une variable dynamique tandis que setq est utilisé pour assigner une valeur à une variable dynamique ou lexicale. La valeur d'une variable dynamique est recherchée dans l'environnement qui appelle la fonction, tandis que la valeur d'une variable lexicale est recherchée dans l'environnement où la fonction a été définie. L'exemple suivant clarifiera la différence:

;; 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.

Les variables dynamiques sont utiles pour passer une valeur par défaut. Par exemple, nous pouvons lier la variable dynamique *out* à la sortie standard, pour qu'elle devienne la sortie par défaut de toutes les fonctions io. Pour remplacer ce comportement, nous introduisons simplement une liaison locale:

> (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

Une utilisation courante des variables lexicales consiste à définir des fermetures, pour émuler des objets avec un état. Dans le premier exemple, la variable y dans l'environnement de liaison de fy est effectivement devenue l'état privé de cette fonction.

defvar affectera une valeur à une variable uniquement si elle n'est pas déjà affectée. Ainsi, la redéfinition suivante de *x* ne modifiera pas la liaison d'origine:

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

Nous pouvons attribuer une nouvelle valeur à *x* en utilisant 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
18
Vijay Mathew

DEFVAR établit une nouvelle variable. SETQ assigne à une variable.

La plupart des implémentations LISP que j'ai utilisées émet un avertissement si vous définissez une variable qui n'existe pas encore.

8
Ken

defvar et defparameter introduisent tous deux des variables globales. Comme le note Ken, setq assigne à une variable.

De plus, defvar n'encombrera pas quelque chose auparavant defvar- ed. Seibel dit plus loin dans le livre (Chapitre 6): "En pratique, vous devez utiliser DEFVAR pour définir les variables qui contiendront les données que vous souhaitez conserver même si vous avez modifié le code source qui utilise la variable."

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

Par exemple, si vous avez un *db* Global pour la base de données dans le chapitre Base de données simple:

(defvar *db* nil)

... et vous commencez à jouer avec lui au REPL - ajout, suppression de choses, etc. - mais ensuite vous apportez une modification au fichier source qui contient cette forme defvar, le rechargement de ce fichier ne sera pas effacez *db* et toutes les modifications que vous pourriez avoir apportées ... Je crois que setq le fera, tout comme defparameter. Un Lisper plus expérimenté, corrigez-moi si je suis mal cependant.

8
michiakig