Ответ
Вы можете написать макрос для обхода кода, который заменяет ссылки на несвязанные переменные на кавычки, если у вас есть достаточный доступ на время расширения макроса к лексическому контексту, чтобы определить, какие переменные лексически не связаны. При программировании с помощью желаемого мышления макрос может выглядеть примерно так:
(defmacro with-ad-hoc-quotations (&body forms &environment outer-env)
(walk:map-variable-references
(lambda (var env)
(if (walk:lexically-bound-p var env)
var
`(if (boundp ',var) ;deal with dynamic bindings
(symbol-value ',var)
',var)))
`(progn ,@forms)
outer-env))
где walk:map-variable-references
- это функция, которая принимает форму и заменяет все ссылки на переменные в предоставленной форме результатом применения предоставленной функции к имени переменной и лексическому контексту; и где предполагается, что walk:lexically-bound-p
возвращает обобщенное логическое значение, которое сообщает вам, является ли данный символ лексически связанным в данном контексте.
Вам нужно будет самостоятельно найти реализации последних функций (а walk:lexically-bound-p
может потребоваться поддержка со стороны реализации Lisp), но это общая идея. Использование макроса будет следующим:
(with-ad-hoc-quotations (l a a))
;=> (A A)
или, более интересный пример,
(defvar *c* 20)
(with-ad-hoc-quotations
(let ((b 10))
(list a b *c*)))
;=> (A 10 20)
Реализация в SBCL
В качестве подтверждения концепции это специфичная для SBCL реализация, использующая sb-walker
:
(defmacro with-ad-hoc-quotations (&body forms &environment outer-env)
(sb-walker:walk-form
`(progn ,@forms)
outer-env
(lambda (form ctx env)
(declare (ignore ctx))
(typecase form
(symbol
(if (sb-walker:var-lexical-p form env)
form
`(if (boundp ',form)
(symbol-value ',form)
',form)))
(t
form)))))
Или, если вы предпочитаете более общую версию, описанную выше, мы можем реализовать две необходимые функции для SBCL следующим образом:
#+sbcl
(defun lexically-bound-p (var env)
(sb-walker:var-lexical-p var env))
#+sbcl
(defun map-variable-references (fn form &optional env)
(sb-walker:walk-form form
env
(lambda (expr ctx env)
(declare (ignore ctx))
(typecase expr
(symbol (funcall fn expr env))
(t expr)))))
Реализация с использованием hu.dwim.walker
Наконец, ниже приведена реализация, основанная на переносимой библиотеке hu.dwim.walker
(доступна через Quicklisp ). Помните, что не не учитывает лексический контекст, включающий его, поэтому вам, как правило, нужно вызывать его на верхнем уровне, чтобы он работал правильно.
(defmacro with-ad-hoc-quotations (&body forms)
(hu.dwim.walker:unwalk-form
(hu.dwim.walker:rewrite-ast
(hu.dwim.walker:walk-form `(progn ,@forms))
(lambda (parent type form)
(declare (ignore parent type))
(typecase form
(hu.dwim.walker:free-variable-reference-form
(hu.dwim.walker:walk-form `',(hu.dwim.walker:name-of form)))
(t
form))))))