#lisp #common-lisp #dynamic-binding
#lisp #common-lisp #динамическая привязка
Вопрос:
Я только что столкнулся с необычной ситуацией в моем common lisp-коде, когда я хочу протестировать locally
и declare
:
(defvar test-out 2) ;; make a dynamic variable
;; function below just simply re-write from locally doc
(defun test (out)
(declare (special out))
(let ((out 1))
(print out) ;; => 1
(print (locally (declare (special out)) out)))) ;; => 2
;; when argument has same name as outside dynamic variable
(defun test1 (test-out)
(declare (special test-out))
(let ((test-out 1))
(print test-out) ;; => 1
(print (locally (declare (special test-out)) test-out)))) ;; => also 1
Я знаю, что правильное имя динамической переменной должно быть *test-out*
, но я подумал, что программисту удобно указывать динамическую переменную.
Меня немного смущает test1
функция, похоже locally declare
, не указывает test-out
на динамическую переменную снаружи.
Кто-нибудь может объяснить мне test1
поведение функции? Спасибо
Обновить:
- Я даю новую динамическую переменную
(defvar test-out-1 3)
и вызываю ее как(test1 test-out-1)
, все равно получаю результат печати1
и1
. - Я меняю
test1
имя аргумента сtest-out
наtest-out1
, перекомпилируюtest1
, и проблема исчезает, распечатываю результаты1
и2
при вызове(test1 test-out)
. - Я меняю
(defvar test-out 2)
на(defvar test-out-1 2)
(изменить имя динамической переменной). Затем перекомпилируйте весь файл (test-out
на этот раз динамическая переменная не вызывается, аtest1
имя аргумента равноtest-out
), проблема исчезнет. - После 3 я вызываю
(defvar test-out 2)
, и(test1 test-out)
. На этот раз он выводит правильные ответы:1
и2
. - После 4 я
test1
снова перекомпилирую, затем запускаю(test1 test-out)
, он распечатывается1
, и1
снова появляется проблема.
Если я правильно догадываюсь, при test1
компиляции по какой-то причине имя ее аргументов подключается к динамической переменной test-out
. Вот почему я получаю неверный результат, когда я даже вызываю с другим значением, однако проблема решается сама собой, когда я перекомпилирую test1
с другим именем аргумента или очищаю динамическую переменную test-out
перед повторной компиляцией теста.
Если это так, я все еще не понимаю, почему функция компиляции будет действовать с помощью динамической переменной в среде.
Ответ №1:
DEFVAR
объявляет переменную специальной — это означает, что они будут использовать динамические привязки при привязке, и доступ к такой переменной будет искать динамические привязки. Глобально и на всех уровнях привязки. На данный момент и в будущем.
С этого момента ВСЕ виды использования и привязки этой переменной в новом коде будут автоматически объявлены специальными. Даже локальные привязки LET. На всех уровнях. Невозможно объявить ее неспециалистом. Таким образом, локальное специальное объявление в вашей test1
функции теперь не требуется, оно уже объявлено специальным. Каждое ее использование или привязка, даже без явного объявления, теперь использует динамическую привязку.
Это также причина, по которой любая DEFVAR
DEFPARAMETER
переменная or должна быть записана как *variablename*
, чтобы предотвратить случайное объявление всех переменных с одинаковым именем как специальных.
Избегайте:
(defvar x 10) ; here X is explicitly declared special
(defun foo (x) ; here X is implicitly declared special
(let ((x ...)) ; here X is implicitly declared special
...))
Сделать:
(defvar *x* 10) ; here *X* is declared special
(defun foo (x) ; here X is lexical
(let ((x ...)) ; here X is lexical
...))
Комментарии:
1. Итак, для моего обновления 4
(defvar test-out 2)
послеtest1
компиляции не будет действоватьtest1
, потому что оно уже скомпилировано?2. @ccQpein: правильно, DEFVAR обычно не влияет на ранее скомпилированный код. Если ранее скомпилированный код был скомпилирован для использования лексической привязки, это не будет изменено позже, когда DEFVAR будет скомпилирован / загружен / выполнен.
3. @Rainer Joswig, Не было бы также приемлемо сделать:
(defvar *x* 10) (defun foo () (let ((*x* ...)) ...))