2010-10-04 2 views
39

Я вижу, что Practical Common Lisp использует (defvar *db* nil) для настройки глобальной переменной . Не так ли использовать setq с той же целью?setq и defvar в Lisp

Каковы преимущества/недостатки использования defvar против setq?

ответ

38

Есть несколько способов ввести переменные.

DEFVAR и DEFPARAMETER ввести глобальные динамические переменные. DEFVAR необязательно задает его до некоторого значения, если оно уже не определено. DEFPARAMETER всегда задает значение. SETQ не вводит переменную.

(defparameter *number-of-processes* 10) 

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

Обратите внимание, что вы, вероятно, никогда не хотят DEFVAR переменных с такими именами, как x, y, stream, limit ... Почему? Потому что эти переменные тогда будут объявлены специальными, и их трудно отменить. Специальное объявление является глобальным, и все дальнейшие применения переменной будут использовать динамическое связывание.

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 

ЛУЧШЕ: Всегда помечать специальные переменные с * в их имена!

(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 

Локальные переменные вводятся с DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND и многие другие.

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

Теперь по умолчанию локальные переменные в этих двух формах являются лексические, если они не объявлены SPECIAL. Тогда они будут динамическими переменными.

Далее, есть несколько форм для установки переменной в новые значения.SET, SETQ, SETF и другие. SETQ и SETF могут устанавливать как лексические, так и специальные (динамические) переменные.

Требуется, чтобы переносимый код устанавливал переменные, которые уже были объявлены. Точный эффект установки не объявленной переменной не определен стандартом.

Так что, если вы знаете, что делает ваш Common Lisp реализации, вы можете использовать

(setq world (make-new-world)) 

в Read-Eval-Print-Loop на верхнем уровне. Но не используйте его в своем коде, так как эффект не переносимый. Обычно SETQ задает переменную. Но некоторые реализации могут также объявить переменную SPECIAL, когда она не знает этого (CMU Common Lisp делает это по умолчанию). Это почти всегда не то, что нужно. Используйте его для случайного использования, если вы знаете, что делаете, но не для кода.

То же самое здесь:

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

Во-первых, такие переменные должны быть записаны в виде *world* (с окружающими * символов), чтобы понять, что это глобальная специальная переменная. Во-вторых, он должен был быть объявлен с DEFVAR или DEFPARAMETER.

Типичный компилятор Lisp будет жаловаться на то, что вышеуказанная переменная не объявлена. Поскольку глобальные лексические переменные не существуют в Common Lisp, компилятор должен генерировать код для динамического поиска. Некоторые компиляторы тогда говорят, хорошо, мы предполагаем, что это динамический поиск, давайте объявим его специальным - так как это то, что мы предполагаем в любом случае.

+1

Я удивлен, что как только вы объявили переменную глобальной с defvar, вы не можете создать локальную переменную с тем же именем, поэтому (пусть ((x 1)) x) может вызвать неожиданные результаты, если x объявляется defvar. – Ian

+1

@ian Это одна из причин, почему многие люди используют «ушные муфты» (то есть они никогда не используют '(defvar x foo)', они используют '(defvar * x * foo)', таким образом, вы гораздо менее склонны к сделать ошибку. – Vatine

7

DEFVAR устанавливает новую переменную. SETQ присваивает переменной.

Большинство реализованных Lisp-реализаций выдаст предупреждение, если вы установили SETQ в переменную, которая еще не существует.

7

defvar и defparameter оба представляют глобальные переменные. Как отмечает Кен, setq присваивает переменной.

Кроме того, defvar не будет сбивать что-то ранее defvar -ed. Сейбл говорит позже в книге (глава 6): «Практически говоря, вы должны использовать DEFVAR для определения переменных, которые будут содержать данные, которые вы хотите сохранить, даже если вы внесли изменения в исходный код, который использует переменную».

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

Например, если у вас есть глобальный *db* для базы данных в простой главе базы данных:

(defvar *db* nil) 

... и вы начинаете играть с ним в РЕПЛЕ - добавление, удаление вещи и т. д. - но затем вы вносите изменения в исходный файл, который содержит эту форму defvar, перезагружая этот файл, не уничтожит *db* и все изменения, которые вы могли бы сделать ... Я считаю, что setq будет, как и defparameter. Более опытный Лиспер, пожалуйста, поправьте меня, если я ошибаюсь.

16

defvar вводит динамическую переменную, а setq используется для назначения значения динамической или лексической переменной. Значение динамической переменной просматривается в среде, вызывающей функцию, а значение лексической переменной просматривается в среде, где определена функция. В следующем примере разница будет разной:

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

Динамические переменные полезны для передачи значения по умолчанию. Например, мы можем привязать динамическую переменную *out* к стандартным выводам, чтобы она стала стандартным выходом всех функций io. Чтобы изменить это поведение, мы просто ввести локальное связывание:

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

Обычного использование лексических переменных в определении замыканий, эмулировать объекты с государством. В первом примере переменная y в среде привязки fy фактически стала частным состоянием этой функции.

defvar присвоит значение переменной, только если она еще не назначена.Таким образом, следующее переопределение *x* не изменит оригинал связывания:

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

Мы можем присвоить новое значение *x* с помощью 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

К сожалению, это неправильно. Точный эффект (setq y 200) на не объявленной/определенной переменной не определен. Common Lisp также не имеет глобальных лексических переменных. SETQ устанавливает переменную. Больше ничего. Либо динамическая переменная, либо лексическая переменная, в зависимости от предоставленной переменной. LET связывается. SETQ. –

+0

Также нельзя определить функцию CL: PRINT, так как это имя уже принято стандартной функцией. FORMAT печатает в потоках, а не в файлах. –

+0

@Rainer Спасибо за указание на неточности. Я обновил ответ. –

Смежные вопросы