Ответ 1
Существует несколько способов ввода переменных.
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
,... Почему? Потому что эти переменные тогда будут объявлены специальными, и их трудно отменить. Специальное объявление является глобальным, и все дальнейшие применения переменной будут использовать динамическое связывание.
ПЛОХО:
(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
могут устанавливать как лексические, так и специальные (динамические) переменные.
Для переносимого кода требуется, чтобы один задавал переменные, которые уже были объявлены. Точный эффект установки не объявленной переменной undefined по стандарту.
Итак, если вы знаете, что делает реализация 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, компилятор должен генерировать код для динамического поиска. Некоторые компиляторы тогда говорят, хорошо, мы предполагаем, что это динамический поиск, пусть объявит его особенным - так как это то, что мы все равно принимаем.