Common Lisp - CCL, почему предупреждение при передаче глобальной функции в локальную функцию?

Я изучаю Common Lisp, используя CCL. Я получаю предупреждение, когда использую глобальную переменную локально. Почему CCL обеспечивает эту функциональность? Какова цель этого?

(setf n 75)

;;;This function works, but gets a warning about
;;;n being an undeclared free variable.
(defun use-global ()

(write n)
)

;;;This function works without warnings.
(defun global-to-local (x)

(write x)
)

(use-global)
(global-to-local n)

3 ответа

Решение

Setf а также setq не вводите новые переменные, они изменяют существующие.

Чтобы определить что-то вроде глобальных переменных, используйте defvar или же defparameter, Они условно написаны с ведущими и заканчивающимися *и они автоматически объявляются глобально особенными. Это означает, что всякий раз, когда вы перепривязываете их, эта привязка действует для всего стека вызовов над ним, независимо от того, какие функции вы вызываете оттуда и т. Д.

В вашем примере, уровень setf не делал этого, поэтому при компиляции функции use-globalне видит компилятор n подразумевается как таковой и выдает предупреждение. В правильном и идиоматическом коде Common Lisp это предупреждение обычно следует рассматривать как ошибку, так как оно затем указывает на опечатку или опечатку.

Предупреждение дает мало значения. Переменная связана с предыдущим назначением верхнего уровня, которое создает привязку в глобальной среде, делая эту переменную глобальной переменной. Это просто доступ, что, скорее всего, и предназначено программистом.

Предупреждение о несвязанной переменной очень ценно, когда не определено определение переменной, потому что оно перехватывает орфографические ошибки ссылок на переменные. Но на высшем уровне setf или же setq должны быть замечены реализацией и рассматриваться как определение.

Полезно иметь предупреждение для случаев, когда переменная определяется верхним уровнем setf а затем позже при условии let:

(setf n 42)

(let ((n 43)) (func))

Здесь, похоже, программист может ожидать n быть специальной переменной, но она не была определена таким образом. func увидим привязку верхнего уровня n который держит 42, а не лексический n который содержит 43. Таким образом, есть веская причина для диагностики здесь. Цель кода требует n быть объявленным через defvar или же defparameterили провозглашен особенным.

(Конечно, мы не можем предупредить о (let ((n 43)) ...) когда нет верхнего уровня n было замечено, потому что это обычная ситуация для подавляющего большинства лексических переменных.)

В ANSI Common Lisp есть небольшой недостаток, заключающийся в том, что в разделе 3.1.2.1.1 Символы в виде форм говорится, что существует только три вида переменных: динамические, лексические и постоянные. Динамическая переменная - это переменная, объявленная особой, и поэтому в соответствии с этим рассуждением, setf недостаточно для создания динамической переменной. Тем не менее, в разделе 3.1.1.1 четко прописано, что существует глобальная среда. Смущает, что он говорит, что он содержит привязки, которые имеют неопределенный размер и область действия, и что он содержит динамические переменные. Но тогда "динамическая переменная" определяется в глоссарии как переменная с привязкой в ​​динамической среде, а динамическая среда определяется как содержащая привязки с "динамическим экстентом". Так что это не может быть глобальной средой. Вы знаете, что тот, который говорит 3.1.1.1, содержит динамические переменные!

Во всяком случае, все это заблуждение ANSI создало неправильное представление, что setf или же setq не может создать переменную, которая поддерживает ложную диагностику компилятора.

Быстрое прибегание к помощи подсказывает, что в Common Lisp это делается примерно так:

(defvar *n* 75)

(defun use-global () (write *n*))

(use-global)

Обратите внимание на звездочку, которая по соглашению украшает глобальные имена.

Другие вопросы по тегам