Локальные переменные в макрорасширениях
В предыдущем разделе определение for было исправлено следующим образом, чтобы расширение оценивало аргументы макроса нужное количество раз:
(defmacro for (var from init to final do &rest body) "Execute a simple for loop: (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)
"Execute a simple for loop: (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, который обычно появляется в выражениях.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Surprising-Local-Vars.html