57 votes

setq et defvar dans lisp

Je vois que le Common Lisp pratique utilise (defvar * db * nil) pour configurer la variable globale. N'est-il pas acceptable d'utiliser setq aux mêmes fins?

Quels sont les avantages / inconvénients d'utiliser defvar vs setq?

57voto

Rainer Joswig Points 62532

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

DEFVAR et DEFPARAMETER introduire global des variables dynamiques. DEFVAR éventuellement à une certaine valeur, sauf s'il est déjà défini. DEFPARAMETER définit-il toujours à la valeur fournie. SETQ ne pas introduire une variable.

(defparameter *number-of-processes* 10)

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

Les variables locales sont introduits avec DEFUN, LAMBDA, LAISSER les, MULTIPLES-LIER et 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 lexicale, sauf si elles sont déclarées SPÉCIALES. Alors ils seraient des variables dynamiques.

Ensuite, il y a aussi plusieurs formes de définir une variable à de nouvelles valeurs. ENSEMBLE, SETQ, SETF et les autres. SETQ et SETF peut définir à la fois lexicale et spéciaux (dynamique) des variables.

Il est nécessaire pour portable code que l'on définit les variables qui sont déjà déclarés. L'effet exact de l'établissement d'un pas déclaré la variable n'est pas définie par la norme.

Donc, si vous savez ce que la Common Lisp mise en œuvre n', vous pouvez utiliser

(setq world (make-new-world))

dans le Read-Eval-Print-Boucle au niveau supérieur. Mais ne pas l'utiliser dans votre code, car l'effet n'est pas portable. Généralement SETQ la variable sera définie. Mais certains de la mise en œuvre pourrait aussi déclarer la variable SPÉCIALE lorsqu'il ne le sait pas (CMUCL fait que, par défaut). C'est presque toujours pas ce que l'on voudrait. L'utiliser pour un usage occasionnel si vous savez ce que vous faites, mais pas pour le code.

Même ici:

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

Tout d'abord, ces variables doivent être écrites comme *monde* (avec les environs * caractères), pour faire comprendre que c'est un global variable spéciale. Deuxièmement, il doit avoir été déclaré auprès de DEFVAR ou DEFPARAMETER avant.

Typique d'un compilateur Lisp va se plaindre que ci-dessus est une variable non déclarée. Puisque les variables lexicales n'existe pas en Common Lisp, le compilateur doit générer du code pour une recherche dynamique. Certains 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.

22voto

Vijay Mathew Points 17155

defvar introduit une variable dynamique tout en setq est utilisé pour affecter une valeur à une dynamique ou une variable 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 va faire 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 le passage autour d'une valeur par défaut. Par exemple, nous pouvons lier la dynamique de la variable *out* à la sortie standard, de sorte qu'il devient la sortie par défaut de toutes les e / s fonctions. Pour remplacer ce comportement, nous venons d'introduire 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 de lexique des variables est dans la définition de fermetures, à imiter des objets avec l'état. Dans le premier exemple, la variable y dans la liaison de l'environnement de l' fy est devenu effectivement le privé, l'état de la fonction.

defvar va assigner une valeur à une variable seulement si elle n'est pas déjà attribué. Donc la suite de la re-définition de l' *x* ne changera pas l'original de liaison:

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

On peut lui assigner une nouvelle valeur à l' *x* par l'aide d' 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

9voto

Ken Points 924

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.

8voto

spacemanaki Points 4485

defvar et defparameter à la fois d'introduire des variables globales. Comme Ken notes, setq assigne à une variable.

En outre, defvar ne sera pas supprimé quelque chose, ce précédemment defvar-ed. Seibel dit plus tard dans le livre (Chapitre 6): "en pratique, vous devez utiliser DEFVAR de définir des variables qui contiennent les données que vous voulez garder même si vous avez fait une modification du code source qui utilise la variable."

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

Par exemple, si vous avez un mondial *db* de la base de données dans la Base de données Simple chapitre:

(defvar *db* nil)

...et vous commencez à jouer avec elle à la REPL - ajout, suppression, etc - mais alors, vous en modifiez le fichier source qui contient la defvar forme, le rechargement de ce fichier ne sera pas essuyer *db* et tous les changements que vous avez fait... je crois qu' setq sera, comme defparameter. Un de plus expérimenté Lisper s'il vous plaît corrigez-moi si je me trompe bien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X