x
, привязанный к значению y
, означает, что x
- это новая привязка, которая получает копию того же значения, что и y
.x
и y
не являются псевдонимами для общей памяти.
Хотя из-за проблем оптимизации привязки не являются точными ячейками памяти, вы можете смоделировать их поведение таким образом.Другими словами, вы можете рассматривать среду как мешок мест хранения, обозначаемых символами.
Оценщики образовательных схем в схеме, фактически, используют списки ассоциаций для представления сред.Таким образом, (let ((x 1) (y 2)) ...)
создает среду, которая выглядит просто как ((y . 1) (x . 2))
.Места хранения - это поля cdr
пар cons
в этом списке, а их метки - это символы в полях car
.Сама клетка является связующим;символ и местоположение связаны друг с другом благодаря тому, что находятся в одной и той же структуре cons
.
Если вокруг этого let
существует внешняя среда, то эти пары ассоциаций могут быть просто добавлены в нее с помощью cons:
(let ((z 3))
;; env is now ((z . 3))
(let ((x 1) (y 2))
;; env is now ((y . 2) (x . 1) (z . 3))
Среда - это просто набор привязок, на которые мы нажимаем.Когда мы фиксируем лексическое замыкание, мы просто берем текущий указатель и прячем его в объект замыкания.
(let ((z 3))
;; env is now ((z . 3))
(let ((x 1) (y 2))
;; env is now ((y . 2) (x . 1) (z . 3))
(lambda (a) (+ x y z a))
;; lambda is an object with these three pices:
;; - the environment ((y . 2) (x . 1) (z . 3))
;; - the code (+ x y z a)
;; - the parameter list (a)
)
;; after this let is done, the environment is again ((z . 3))
;; but the above closure maintains the captured one
)
Итак, предположим, что мы вызываем lambda
с аргументом 10. Лямбда принимает список параметров (a)
и связывает его со списком аргументов для создания новой среды:
((a . 1))
Эта новая среда не создана в вакууме;он создается как расширение захваченной среды.Итак, действительно:
((a . 1) (y . 2) (x . 1) (z . 3))
Теперь, в этой эффективной среде, выполняется тело (+ x y z a)
.
Все, что вам нужно понять о средах, можно понять применительно к этой паре минусов.модель привязок.
Присвоение переменной?Это просто set-cdr!
для связывания на основе минусов.
Что такое "расширение среды"?Это просто выдвигает основанную на минусах привязку на фронт.
Что такое «свежая привязка» переменной?Это просто выделение новой ячейки с (cons variable-symbol value)
и расширение с ней среды путем ее включения.
Что такое «затенение» переменной?Если среда содержит (... ((a . 2)) ...)
, и мы помещаем новую привязку (a . 3)
в эту среду, то эта a
теперь видима, а (a . 2)
скрыта просто потому, что функция assoc
выполняет линейный поиск и находит (a . 2)
первый!Подбор внутренней и внешней среды идеально моделируется assoc
.Внутренние привязки появляются слева от внешних привязок, ближе к началу списка и находятся первыми.
Семантика совместного использования всех вытекает из семантики этих списков ячеек.В модели списка ассоциаций совместное использование среды происходит, когда два списка ассоциаций среды совместно используют один и тот же хвост.Например, каждый раз, когда мы называем нашу лямбду выше, создается новая среда аргументов (a . whatever)
, но она расширяет тот же хвост захваченной среды.Если лямбда изменится a
, это не будет видно из других вызовов, но если она изменит x
, то другие вызовы увидят ее.a
является частным для лямбда-вызова, но x
, y
и z
являются внешними по отношению к лямбде в захваченной среде.
Если вы мысленно прибегаете к этой модели списка ассоциацийвы не ошибетесь, если будете определять поведение окружения, включая произвольно сложные ситуации.
Реальные реализации в основном просто оптимизируются вокруг этого.например, переменная, которая инициализируется из константы типа 42
и никогда не назначается, вообще не должна существовать как фактическая запись среды;оптимизация, называемая «постоянным распространением», может просто заменить вхождения этой переменной на 42
, как если бы это был макрос.Реальные реализации могут использовать хеш-таблицы или другие структуры для уровней среды, а не связанные списки.Реальные реализации могут быть скомпилированы: лексические среды могут быть скомпилированы в соответствии с различными стратегиями, такими как «преобразование замыкания».По сути, вся лексическая область видимости может быть сведена в один вектороподобный объект.Когда замыкание выполняется во время выполнения, весь вектор дублируется и инициализируется.Скомпилированный код ссылается не на переменные символы, а на смещения в векторе замыкания, что значительно быстрее: линейный поиск по списку ассоциаций не требуется.