Common Lisp - CCL, почему предупреждение при передаче глобальной функции в локальную функцию? - PullRequest
0 голосов
/ 13 сентября 2018

Я изучаю 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 ]

0 голосов
/ 13 сентября 2018

Setf и setq не вводят новые переменные, они модифицируют существующие.

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

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

0 голосов
/ 14 сентября 2018

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

Предупреждение о несвязанной переменной очень ценно, когда не определено определение переменной, потому что оно перехватывает орфографические ошибки ссылок на переменные. Но реализация 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 не может создать переменную, которая поддерживает ложную диагностику компилятора.

0 голосов
/ 13 сентября 2018

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

(defvar *n* 75)

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

(use-global)

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

...