В предыдущем разделе определение for было изменено следующим образом, чтобы расширение вычисляло аргументы макроса нужное количество раз:
(defmacro for (var from init to final do &rest body) "Выполнить простой цикл for: (for i from 1 to 10 do (print i))."
`(let ((,var ,init)
(max ,final))
(while (<= ,var max)
,@body
(inc ,var))))
Новое определение for имеет новую проблему: оно вводит локальную переменную с именем max, которую пользователь не ожидает. Это вызывает проблемы в таких примерах, как следующий:
(let ((max 0))
(for x from 0 to 10 do
(let ((this (frob x)))
(if (< max this)
(setq max this)))))
Ссылки на max внутри тела for, которые должны ссылаться на привязку max пользователя, на самом деле обращаются к привязке, созданной for.
Способ исправления этого — использовать неинтернируемый символ вместо max (см. Создание и интернирование символов). Неинтернируемый символ можно привязать и на который можно ссылаться, как и на любой другой символ, но поскольку он создается for, мы знаем, что он не может уже присутствовать в программе пользователя. Поскольку он не интернирован, нет способа, которым пользователь может добавить его в программу позже. Он никогда не появится нигде, кроме как там, где его поместил for. Вот определение for, которое работает таким образом:
(defmacro for (var from init to final do &rest body)
"Выполнить простой цикл for: (for i from 1 to 10 do (print i))."
(let ((tempvar (make-symbol "max")))
`(let ((,var ,init)
(,tempvar ,final))
(while (<= ,var ,tempvar)
,@body
(inc ,var)))))
Это создает неинтернируемый символ с именем max и помещает его в расширение вместо обычного интернированного символа max, который появляется в выражениях обычно.