Общая область видимости Lisp (динамическая и лексическая)
EDIT: я изменил код примера после первого ответа, потому что я придумал простую версию, которая вызывает те же вопросы.
в настоящее время я изучаю свойства области видимости Common Lisp. После того, как я подумал, что у меня есть твердое понимание, я решил кодировать некоторые примеры, которые я мог бы предсказать результат, но, очевидно, я был неправ. У меня есть три вопроса, каждый из которых относится к приведенному ниже примеру:
Пример 1:
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
выход:
5
*** - EVAL: variable X has no value
вопрос: это имеет смысл. x статически ограничен, и fun2 не может найти значение x без его явной передачи.
Пример 2:
(defvar x 100)
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
выход:
5
5
вопрос: я не понимаю, почему x внезапно виден fun2 со значением, которое fun1 дал ему, вместо того, чтобы иметь значение 100...
Пример 3:
(setf x 100)
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
выход:
5
100
Вопрос: Должен Ли Я игнорировать эти результаты, так как вызов setf для необъявленной переменной, по-видимому, не определен? Это то, чего я ожидал бы во втором примере...
любое понимание будет высоко оценен...
1 ответов
эффекты установки неопределенной переменной с помощью SETF не определены в ANSI Common Lisp.
DEFVAR определит специальную переменную. Эта декларация является глобальной и также влияет на привязки LET. Вот почему по соглашению эти переменные записываются как *foo*
. Если вы когда-либо определяли X с помощью DEFVAR, он объявляется специальным, и нет способа объявить его лексическим позже.
LET по умолчанию предоставляет локальные лексические переменные. Если переменная была уже объявлен специальный (например, из-за DEFVAR), то он просто создает новую локальную динамическую привязку.
обновление
- Пример 1 .
ничего не видно.
- Пример 2
X
объявлен особый. Во всех случаях использования переменной X теперь используется динамическая привязка.
При вызове функции вы привязываете X к 5. Динамично. Другие функции теперь могут получить доступ к этой динамической привязки и получить это значение.
- Пример 3
это неопределенное поведение в Common Lisp. Вы устанавливаете необъявленную переменную. То, что происходит потом, зависит от реализации. Ваша реализация (большинство из них делают что-то подобное) устанавливает значение символа X в 100. В FUN1 X лексически связан. В fun2 оценка X извлекает значение символа (или, возможно, динамически связанное значение) X.
в качестве примера для реализации что делал (делает?) что-то еще: реализация CMUCL также должна была бы объявить X в Примере 3 по умолчанию специальным. Установка неопределенной переменной также объявляет ее специальной.
Примечание
в портативном стандартном совместимом коде Common Lisp глобальные переменные определяются с помощью DEFVAR и DEFPARAMETER. Обе эти переменные объявляются особыми. Всех использует эти переменные включают динамическую привязку.
помните:
((lambda (x)
(sin x))
10)
в основном то же самое, что
(let ((x 10))
(sin x))
это означает, что привязки переменных в Let bindings и привязки переменных в вызовах функций работают одинаково. Если X был бы объявлен специальным В каком-то месте ранее, оба будут включать динамическую привязку.
это указано в стандарте Common Lisp. См., например, объяснение специальная декларация.