Что происходит, когда специальная переменная объявляется во время компиляции - PullRequest
2 голосов
/ 22 апреля 2019

Я только что столкнулся с необычной ситуацией в моем обычном коде 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 поведение функции? Спасибо

Обновление:

  1. Я даю новую динамическую переменную (defvar test-out-1 3) и называю ее как (test1 test-out-1), все равно получаю результат печати 1 и 1.
  2. Я изменяю имя аргумента test1 с test-out на test-out1, перекомпилирую test1, и проблема исчезает, результаты распечатки равны 1 и 2, когда я вызываю (test1 test-out).
  3. Я изменяю (defvar test-out 2) на (defvar test-out-1 2) (изменить имя динамической переменной). Затем перекомпилируйте весь файл (на этот раз динамическая переменная не называется test-out, а test1 имя аргумента - test-out), проблема исчезнет.
  4. После 3 я звоню (defvar test-out 2) и (test1 test-out). На этот раз он выводит правильные ответы: 1 и 2.
  5. После 4 я перекомпилирую test1 снова, затем запускаю (test1 test-out), снова выводятся 1 и 1, снова появляется проблема.

Если я угадаю правильно, когда test1 по какой-то причине компилирует, имя его аргумента связывается с динамической переменной test-out. Вот почему я получаю неправильный результат, когда я даже вызываю с другим значением, однако проблема решается сама собой, когда я перекомпилирую test1 с другим именем аргумента или очищаю динамическую переменную test-out перед перекомпиляцией теста.

Если это так, я до сих пор не понимаю, почему функция компиляции влияет на динамические переменные в среде.

1 Ответ

6 голосов
/ 22 апреля 2019

DEFVAR объявляет переменную special - это означает, что они будут использовать динамические привязки при привязке, а доступ к такой переменной будет искать динамические привязки. Глобально и на всех уровнях связывания. Пока и в будущем.

С этого момента ALL использует и привязки этой переменной в новом коде будут объявлены специальными автоматически. Даже местные привязки LET. На всех уровнях. Нет никакого способа объявить это особенным . Таким образом, локальное специальное объявление в вашей функции test1 теперь не нужно, оно уже объявлено специальным. Каждое его использование или привязка, даже без явного объявления, теперь использует динамическое связывание.

Это также причина, по которой любая переменная DEFVAR или DEFPARAMETER должна быть записана как *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
    ...))   

Do:

(defvar *x* 10)       ; here *X* is declared special

(defun foo (x)        ; here X is lexical
  (let ((x ...))      ; here X is lexical
    ...))   
...