2010-10-04 10 views

Odpowiedz

38

Istnieje kilka sposobów wprowadzenia zmiennych.

DEFVAR i DEFPARAMETER wprowadzić globalnych zmiennych dynamicznych. DEFVAR opcjonalnie ustawia ją na pewną wartość, chyba że jest już zdefiniowana. DEFPARAMETER ustawia zawsze na podaną wartość. SETQ nie wprowadza zmiennej.

(defparameter *number-of-processes* 10) 

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

Zauważ, że prawdopodobnie nigdy nie chcą DEFVAR zmiennych o nazwach takich jak x, y, stream, limit ... Dlaczego? Ponieważ te zmienne zostaną uznane za specjalne i trudno je cofnąć. Specjalna deklaracja ma charakter globalny, a wszystkie dalsze zastosowania tej zmiennej wykorzystywałyby wiązanie dynamiczne.

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 

lepiej: oznaczyć zawsze zmienne specjalne * w nazwie!

(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 

Zmienne lokalne są wprowadzane z DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND i wielu innych.

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

Teraz domyślnie lokalne zmienne w dwóch powyższych form są leksykalny, chyba że są one zadeklarowane SPECIAL. Wtedy byłyby zmiennymi dynamicznymi.

Następnie istnieje również kilka formularzy do ustawienia zmiennej na nowe wartości.SET, SETQ, SETF i inne. SETQ i SETF można ustawić zarówno zmienne leksykalne, jak i specjalne (dynamiczne).

Wymagany jest dla przenośnego kodu, który ustawia zmienne, które są już zadeklarowane. Dokładny efekt ustawienia zmiennej nie zadeklarowanej jest niezdefiniowany przez standard.

Tak więc, jeśli wiesz, co robi twój Wspólna realizacja Lisp, można użyć

(setq world (make-new-world)) 

w odczytu-analizy-Print-Loop na Toplevel. Ale nie używaj go w swoim kodzie, ponieważ efekt nie jest przenośny. Zazwyczaj SETQ ustawi zmienną. Ale niektóre implementacje mogą również zadeklarować zmienną SPECIAL, gdy jej nie zna (CMU Common Lisp robi to domyślnie). To prawie zawsze nie jest to, czego byśmy chcieli. Użyj go do swobodnego użytku, jeśli wiesz, co robisz, ale nie kodu.

samo tutaj:

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

Po pierwsze, takie zmienne powinny być zapisane jako *world* (z otaczającymi * znaków), aby wyjaśnić, że jest to specjalna zmienna globalna. Po drugie, powinno być zadeklarowane wcześniej: DEFVAR lub DEFPARAMETER.

Typowy kompilator Lisp będzie narzekał, że powyższa zmienna jest niezadeklarowana. Ponieważ globalne zmienne leksykalne nie istnieją w Common Lisp, kompilator musi wygenerować kod do dynamicznego wyszukiwania. Jakiś kompilator mówi, ok, zakładamy, że jest to dynamiczne wyszukiwanie, zadeklarujmy, że jest to specjalne - ponieważ to jest to, co i tak zakładamy.

+1

Jestem zaskoczony, że po zadeklarowaniu zmiennej jako globalnej z defvar, nie ma możliwości, aby utworzyć lokalną zmienną o tej samej nazwie, więc (let ((x 1) x) może powodować nieoczekiwane wyniki, jeśli x jest deklarowane przez defvar. – Ian

+1

@ian To jeden z powodów, dla którego wiele osób używa "nauszników" (to znaczy, że nigdy nie używają '(defvar x foo)', używają '(defvar * x * foo)', w ten sposób znacznie mniej prawdopodobne, aby zrobić błąd. – Vatine

7

DEFVAR ustanawia nową zmienną. SETQ przypisuje do zmiennej.

Większość Lisp implementacje ja używałem wyśle ​​ostrzeżenie jeśli setq do zmiennej, która jeszcze nie istnieje.

7

defvar i defparameter zarówno wprowadzenie zmiennych globalnych. Jak zauważa Ken, setq przydziela do zmiennej.

Ponadto, defvar nie będzie zrywać czegoś wcześniej defvar -ed. Seibel mówi w dalszej części książki (rozdział 6): "Praktycznie rzecz biorąc, powinieneś używać DEFVAR do definiowania zmiennych, które będą zawierały dane, które chciałbyś zachować, nawet jeśli dokonałeś zmiany w kodzie źródłowym, który używa zmiennej."

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

Na przykład, jeśli masz globalnego *db* dla bazy danych w rozdziale Proste bazy danych:

(defvar *db* nil) 

... i zacząć grać z nim w REPL - dodawanie, usuwanie rzeczy, itp - ale potem wprowadzić zmiany do pliku źródłowego zawierającego że forma defvar, przeładunek, że plik nie będzie wymazać *db* i wszystkie zmiany dokonane mogłeś ... wierzę, że setq będzie, jak będzie defparameter. Bardziej doświadczony Lisper popraw mnie, jeśli się mylę.

16

wprowadza zmienną dynamiczną, a setq służy do przypisania wartości do zmiennej dynamicznej lub leksykalnej. Wartość zmiennej dynamicznej jest sprawdzana w środowisku, które wywołuje funkcję, podczas gdy wartość zmiennej leksykalnej jest sprawdzana w środowisku, w którym funkcja została zdefiniowana. W poniższym przykładzie różnica zostanie wyjaśniona:

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

Zmienne dynamiczne są użyteczne przy przekazywaniu wartości domyślnej. Na przykład możemy powiązać zmienną dynamiczną *out* ze standardowym wyjściem, aby stała się domyślnym wyjściem wszystkich funkcji io. Aby to zmienić, po prostu wprowadzić lokalny Oprawa:

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

Typowym zastosowaniem zmiennych leksykalnych jest w zamknięciach określających, aby naśladować obiekty z państwa. W pierwszym przykładzie zmienna y w środowisku wiążącym fy skutecznie stała się prywatnym stanem tej funkcji.

defvar będzie przypisać wartość do zmiennej tylko wtedy, gdy nie jest już przypisany.Więc po re-definicja *x* nie zmieni oryginał oprawa:

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

Możemy przypisać nową wartość do *x* za pomocą 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 
+4

Niestety to jest złe. Dokładny efekt (setq y 200) dla zmiennej nie zadeklarowanej/zdefiniowanej jest niezdefiniowany. Common Lisp nie ma również globalnych zmiennych leksykalnych. SETQ ustawia zmienną. Nic więcej. Albo zmienna dynamiczna, albo zmienna leksykalna, w zależności od dostarczonej zmiennej. LET wiąże. Zestawy SETQ. –

+0

Również nie można zdefiniować funkcji CL: PRINT, ponieważ ta nazwa jest już zajęta przez funkcję standardową. FORMAT drukuje na strumienie, a nie na pliki. –

+0

@Rainer Dzięki za wskazanie niedokładności. Zaktualizowałem odpowiedź. –