2010-10-04 9 views
39

Ich sehe, dass die Practical Common Lisp(defvar *db* nil) für die Einrichtung einer globalen Variablen verwendet. Ist es nicht in Ordnung, setq für den gleichen Zweck zu verwenden?setq und defvar in Lisp

Was sind die Vor-/Nachteile der Verwendung von defvar vs. setq?

Antwort

38

Es gibt mehrere Möglichkeiten, Variablen einzuführen.

DEFVAR und DEFPARAMETER einführen globale dynamische Variablen. DEFVAR legt optional einen Wert fest, sofern es nicht bereits definiert ist. DEFPARAMETER setzt es immer auf den angegebenen Wert. SETQ keine Variable einzuführen.

(defparameter *number-of-processes* 10) 

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

Beachten Sie, dass Sie wahrscheinlich nie wie DEFVAR Variablen mit Namen wollen x, y, stream, limit, ... Warum? Weil diese Variablen dann als speziell deklariert werden und es schwierig ist, das rückgängig zu machen. Die spezielle Deklaration ist global und alle weiteren Verwendungen der Variablen würden eine dynamische Bindung verwenden.

BAD:

(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 

Besser: Immer spezielle Variablen mit * in ihrem Namen markieren!

(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 

Lokale Variablen werden eingeführt mit DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND und viele andere.

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

nun standardmäßig die lokalen Variablen in beiden oben genannten Formen sind lexikalische, es sei denn, sie SPECIAL deklariert sind. Dann wären sie dynamische Variablen.

Als nächstes gibt es auch mehrere Formen eine Variable, um neue Werte zu setzen.SET, SETQ, SETF und andere. SETQ und SETF können sowohl lexikalische und spezielle (dynamisch) Variablen gesetzt.

Es ist für den portablen Code erforderlich, die eine Variable setzt, die bereits deklariert sind. Der genaue Effekt des Festlegens einer nicht deklarierten Variablen ist vom Standard nicht definiert.

Also, wenn Sie wissen, was Ihre Common Lisp Implementierung der Fall ist, können Sie

(setq world (make-new-world)) 

im Read-Eval-Print-Loop- im Toplevel verwenden. Aber benutze es nicht in deinem Code, da der Effekt nicht tragbar ist. Normalerweise wird SETQ die Variable festlegen. Aber einige Implementierung könnte auch erklären, die Variable SPECIAL wenn sie es nicht weiß (CMU Common Lisp tut das Standard). Das ist fast immer nicht das, was man möchte. Verwenden Sie es für den gelegentlichen Gebrauch, wenn Sie wissen, was Sie tun, aber nicht für Code.

Hier gilt das gleiche:

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

Zunächst sollten Variablen wie *world* geschrieben werden (mit den umliegenden * Zeichen), um deutlich zu machen, dass es eine globale spezielle Variable ist. Zweitens sollte es zuvor mit DEFVAR oder DEFPARAMETER deklariert worden sein.

Ein typischer Lisp-Compiler wird sich beschweren, dass die obige Variable nicht deklariert ist. Da in Common Lisp keine globalen lexikalischen Variablen vorhanden sind, muss der Compiler Code für eine dynamische Suche generieren. Einige Compiler sagen dann, okay, wir nehmen an, dass dies ein dynamischer Lookup ist, lassen Sie uns es als special deklarieren - da das ist, was wir ohnehin annehmen.

+1

Ich bin überrascht, dass, wenn Sie die Variable global mit Defvar deklariert haben, es keine Möglichkeit gibt, eine lokale Variable mit dem gleichen Namen zu machen, so (x() x) kann zu unerwarteten Ergebnissen führen, wenn x wird von defvar deklariert. – Ian

+1

@ian Das ist ein Grund, warum viele Leute "Ohrenschützer" benutzen (das heißt, sie benutzen nie '(defvar x foo)', sie benutzen '(defvar * x * foo)', auf diese Weise ist es viel weniger wahrscheinlich einen Fehler machen. – Vatine

7

DEFVAR legt eine neue Variable fest. SETQ wird einer Variablen zugewiesen.

Die meisten Lisp-Implementierungen, die ich verwendet habe, werden eine Warnung ausgeben, wenn Sie SETQ auf eine Variable anwenden, die noch nicht existiert.

7

defvar und defparameter beide führen globale Variablen ein. Wie Ken bemerkt, wird setq einer Variablen zugewiesen.

Darüber hinaus defvar wird nicht etwas vorher defvar -ed Clobber. Seibel sagt später in dem Buch (Kapitel 6): "Praktisch gesprochen sollten Sie DEFVAR verwenden, um Variablen zu definieren, die Daten enthalten, die Sie behalten möchten, selbst wenn Sie den Quellcode geändert haben, der die Variable verwendet."

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

Zum Beispiel, wenn Sie haben eine globale *db* für die Datenbank im Simple-Datenbank Kapitel:

(defvar *db* nil) 

... und beginnen Sie mit ihm an der REPL spielen - Hinzufügen, Löschen Dinge, etc - aber dann machen Sie eine Änderung an der Quelldatei, die diese Defvar-Formular enthält, wird das Neuladen dieser Datei nicht *db* und alle Änderungen, die Sie möglicherweise gemacht haben ... Ich glaube, dass setq wird, wie auch defparameter. Ein erfahrener Lisper korrigiert mich bitte, wenn ich mich irre.

16

defvar führt eine dynamische Variable ein, während setq verwendet wird, um einer dynamischen oder lexikalischen Variablen einen Wert zuzuweisen. Der Wert einer dynamischen Variablen wird in der Umgebung nachgeschlagen, die die Funktion aufruft, während der Wert einer lexikalischen Variablen in der Umgebung gesucht wird, in der die Funktion definiert wurde. Das folgende Beispiel macht den Unterschied deutlich:

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

Dynamische Variablen sind nützlich, um einen Standardwert zu übergeben. Zum Beispiel können wir die dynamische Variable *out* an die Standardausgabe binden, so dass sie zur Standardausgabe aller io-Funktionen wird. Um dieses Verhalten zu ändern, führen wir nur eine lokale Bindung:

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

Eine häufige Verwendung von lexikalischen Variablen bei der Definition von Verschlüssen ist, um Objekte mit Zustand zu emulieren. Im ersten Beispiel wurde die Variable y in der Bindungsumgebung von fy effektiv zum privaten Status dieser Funktion.

defvar weist einer Variablen nur dann einen Wert zu, wenn sie nicht bereits zugewiesen ist.So ist die folgende Neudefinition *x* wird nicht die ursprüngliche Bindung ändern:

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

Wir können einen neuen Wert *x* zuweisen, indem setq mit:

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

Leider ist das falsch. Der genaue Effekt von a (setq y 200) auf eine nicht deklarierte/definierte Variable ist nicht definiert. Common Lisp verfügt auch nicht über globale lexikalische Variablen. SETQ legt eine Variable fest. Nichts mehr. Entweder eine dynamische Variable oder eine lexikalische Variable, abhängig von der bereitgestellten Variablen. LET bindet. SETQ-Sätze. –

+0

Auch kann man keine Funktion CL: PRINT definieren, da dieser Name bereits von der Standardfunktion übernommen wird. FORMAT druckt auf Streams, nicht auf Dateien. –

+0

@Rainer Danke für das Aufzeigen der Ungenauigkeiten. Ich habe die Antwort aktualisiert. –