Common Lisp scoping (динамический против лексического)

#dynamic #lisp #common-lisp #scoping #lexical

#динамический #lisp #common-lisp #определение области действия #лексический

Вопрос:

РЕДАКТИРОВАТЬ: я изменил пример кода после первого ответа, потому что я придумал простую версию, которая задает те же вопросы.

В настоящее время я изучаю свойства области видимости 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. Вот мое наивное объяснение после небольшой игры… дайте мне знать, если я близок.. Пример 1: не требует пояснений Пример 2: объявление x как динамической переменной приводит к поиску всех экземпляров x во время выполнения в стеке динамических переменных, что приведет к тому, что fun2 унаследует значение x от fun1, несмотря на то, что он не является динамической переменной. Пример3: понятия не имею… Я думаю, что это не определено

2. Задавать вопросы — это хорошо. Задавать вопросы — это плохо.

3. Вместо DEFMETHOD используйте DEFUN . В этих примерах метод ОПРЕДЕЛЕНИЯ не требуется. DEFUN создает простую функцию. DEFMETHOD предназначен для общих функций, где требуется некоторая диспетчеризация.

Ответ №1:

Последствия установки неопределенной переменной с использованием setf не определены в ANSI Common Lisp.

defvar определит специальную переменную. Это объявление является глобальным и также влияет на let привязки. Это причина, по которой по соглашению эти переменные записываются как *foo* . Если вы когда-либо определяли x с defvar помощью, он объявлен специальным, и нет никакого способа объявить его лексическим позже.

let по умолчанию предоставляет локальные лексические переменные. Если переменная уже была объявлена специальной (например, из-за a defvar ), то она просто создает новую локальную динамическую привязку.

Обновить

  • Пример 1 .

Ничего не видно.

  • Пример 2

x был объявлен специальным. Все виды использования переменной x теперь используют динамическую привязку. При вызове функции вы привязываетесь x к 5 . Динамически. Теперь другие функции могут получить доступ к этой динамической привязке и получить это значение.

  • Пример 3

Это неопределенное поведение в Common Lisp. Вы устанавливаете необъявленную переменную. То, что происходит тогда, зависит от реализации. Ваша реализация (большинство делает что-то подобное) устанавливает значение символа x to 100 . В fun1 , x является лексически связанным. При fun2 оценке x извлекается значение символа (или, возможно, динамически привязанное значение) x .

В качестве примера для реализации, которая сделала (делает?) что-то еще: реализация CMUCL также должна была бы объявлять x в примере 3 по умолчанию особенной. Установка неопределенной переменной также объявляет ее специальной.

ПРИМЕЧАНИЕ

В коде Common Lisp, совместимом с переносимым стандартом, глобальные переменные определяются с defvar помощью и defparameter . Оба объявляют эти переменные особыми. ВСЕ виды использования этих переменных теперь включают динамическую привязку.

Помните:

 ((lambda (x)
   (sin x))
 10)
  

в основном такой же, как

 (let ((x 10))
  (sin x))
  

Это означает, что привязки переменных в let привязках и привязки переменных в вызовах функций работают одинаково. Если x бы это было объявлено специальным в каком-то месте ранее, оба они включали бы динамическое связывание.

Это указано в стандарте Common Lisp. См., Например, Объяснение СПЕЦИАЛЬНОГО объявления.

Комментарии:

1. Я обновил свой первоначальный пост более точными примерами и вопросами, которые отражают то, что мне было трудно понять. В частности, поведение, продемонстрированное в моем втором примере, по сравнению с моим первым примером

2. Спасибо за разъяснение! Я не думаю, что я ожидал, что значения параметров функций будут выполняться так же, как обычные свободные переменные… но я полагаю, что каждое упоминание обрабатывается одинаково.

3. @antman8969: использование переменных везде одинаково. Что немного удивительно, так это то, что параметры функции также могут быть динамически привязаны и что на них влияет определение переменных с помощью DEFVAR .