Локальное динамическое связывание в common lisp

Честно говоря, я не уверен, что полностью понимаю, что значит для привязки быть «динамической» по сравнению с «лексической». Но я понимаю, что когда я использую defvar или defparameter для определения привязки, 1. она объявляет глобальную переменную 2. привязка объявляется «специальной», чтобы ее можно было скрыть новой локальной привязкой, например.

(defvar *x* 3)
(defun f () *x*)
(f) ;=>3
(let ((*x* 2)) (f));=>2

Теперь мой вопрос: возможно ли иметь локальную привязку (т. е. привязку, которая не загрязняет глобальную среду), которая имеет то же свойство (т. е. может быть затенена «внешними»/«более новыми» привязками)?

Пример:

(special-binding ((x 1)) (defun f () x))
(f);=>1
x;=>error, no binding in the global environment
(let ((x 2)) (f));=>2

Я пытался использовать объявления (special x) в блоке let или (locally (declare (special x)) ...), но, похоже, это не создает закрытие (запрос значения переменной из функции, определенной таким образом, вызывает ошибку «Unbound-Variable»).


person Charles Langlois    schedule 09.06.2015    source источник
comment
Замыкания не захватывают специальные привязки, только лексические привязки.   -  person Barmar    schedule 09.06.2015


Ответы (2)


Во-первых, динамическая переменная получает свое значение только из динамических привязок, а не лексических:

(defun f ()
  (declare (special x))
  x)

(let ((x 1))
  ;; without this declaration, we would get an unbound variable error
  (declare (special x))
  (f))
;; => 1

Вы можете получить значение по умолчанию для локальной динамической привязки, используя progv:

(defun b ()
  (progv (if (boundp 'x) () '(x))
      (if (boundp 'x) () '(1))
    (locally (declare (special x))
      x)))

(let ((x 2))
  (declare (special x))
  (b))
;; -> 2

(b)
;; -> 1
person m-n    schedule 09.06.2015
comment
Спасибо, хорошо отвечает на мой вопрос. - person Charles Langlois; 09.06.2015

Вы не можете захватывать динамические привязки в замыкании, только лексические привязки.

Вам нужно объявить переменную специальной в функции, поэтому она будет использовать динамическую привязку:

(defun f () 
  (declare (special x))
  x)

Затем вам нужно динамически связать переменную вокруг вызова с помощью:

(let ((x 1))
  (declare (special x))
  (f))
person Barmar    schedule 09.06.2015
comment
На самом деле объявление special вне let не работает. Цитирование соответствующей части: специальное объявление не влияет на внутренние привязки var; внутренние привязки неявно затеняют специальное объявление и должны быть явно повторно объявлены как специальные. Вы должны были протестировать этот код с x ранее объявленным special глобально, например. через defvar или defparameter. - person acelent; 09.06.2015
comment
Спасибо, просто по памяти, не проверял. - person Barmar; 09.06.2015