setq et defvar en Lisp

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

Quels sont les avantages/inconvénients de l'utilisation de defvar vs setq?

43
demandé sur Rainer Joswig 2010-10-04 17:52:05

4 réponses

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

DEFVAR et DEFPARAMETER introduisent des variables dynamiquesglobales . DEFVAR le définit éventuellement sur une certaine valeur, à moins qu'il ne soit déjà défini. DEFPARAMETER 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 voulez jamais à DEFVAR variables avec des noms comme x, y, stream, limit, ... Pourquoi? Parce que ces variables alors serait déclaré spécial et il est difficile de défaire cela. 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: toujours marquer les variables spéciales avec * dans 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 introduits avec DEFUN, LAMBDA, LAISSEZ, PLUSIEURS VALEUR LIER et beaucoup 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 ci-dessus deux formes sont lexicales, sauf si elles sont déclarées spéciales . Alors ils seraient des variables dynamiques.

Ensuite, il existe également plusieurs formes pour définir une variable à de nouvelles valeurs. ENSEMBLE, SETQ, SETF et d'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 les variables qui sont déjà déclarées. L'effet exact de la définition d'une variable non déclarée est indéfini par la norme.

Donc, si vous savez ce que fait votre implémentation Common Lisp, vous pouvez utiliser

(setq world (make-new-world))

Dans le Read-Eval-Print-Loop au niveau supérieur. Mais ne pas l'utiliser dans votre code, car l'effet n'est pas portable. Typiquement SETQ définira la variable. Mais une implémentation peut également déclarer la variable SPECIAL quand elle ne le sait pas (CMU Common Lisp le fait par défaut). C'est presque toujours pas ce que l'on voudrait. L'utiliser pour un usage occasionnel si vous savoir ce que vous faites, mais pas pour le code.

Même chose ici:

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

Tout d'abord, ces variables doivent être écrites comme *world* (avec les caractères * environnants), pour préciser qu'il s'agit d'une variable spéciale globale. Deuxièmement, il aurait dû être déclaré Avec DEFVAR ou DEFPARAMETER avant.

Un compilateur Lisp typique se plaindra que la variable ci-dessus n'est pas déclarée. Puisque les variables lexicales globales n'existent pas dans Common Lisp, le compilateur doit générer du code pour une recherche dynamique. Quelque compilateur puis dire, d'accord, nous supposons que c'est une dynamique de recherche, nous allons déclarer spécial - puisque c'est ce que nous supposons de toute façon.

42
répondu Rainer Joswig 2017-01-01 15:47:37

defvar introduit une variable dynamique alors que setq est utilisé pour attribuer une valeur à une variable dynamique ou lexicale. La valeur d'une variable dynamique est recherché dans l'environnement qui appelle la fonction, tandis que la valeur d'une variable lexicale est recherché dans l'environnement où la fonction a été définie. L'exemple suivant fera 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, de sorte qu'il devienne la sortie par défaut de toutes les fonctions d'e / s. 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 l'é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 attribuera une valeur à une variable uniquement si elle n'est pas déjà affectée. Donc la suite la redéfinition de *x* ne changera pas la liaison d'origine:

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

, Nous pouvons affecter 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
répondu Vijay Mathew 2010-10-05 05:22:57

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

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

8
répondu Ken 2010-10-04 14:03:03

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

En outre, {[1] } ne sera pas clobber quelque chose précédemment defvar - ed. Seibel dit plus loin dans le livre (Chapitre 6): "pratiquement parlant, vous devriez utiliser DEFVAR pour définir des variables qui contiendront des données que vous voudriez conserver même si vous avez apporté une modification au 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 au REPL-Ajout, Suppression de choses, etc - mais ensuite vous apportez une modification au fichier source qui contient ce formulaire defvar, recharger ce fichier n'effacera pas *db* et tous les changements que vous pourriez avoir faits... Je crois que setq le fera, tout comme defparameter. Un Lisper plus expérimenté veuillez me corriger si je me trompe.

8
répondu michiakig 2010-10-04 14:08:24