Когда это нужно
Связывание даёт результату имя, создаёт локальную область или намеренно сохраняет состояние между вызовами. Обычно достаточно define и let; используйте set!, только если совместное изменение входит в модель.
Проверьте запуском
LISPEX
(define x 1)
(define get (lambda () x))
(set! x 2)
(get)Наблюдаемый результат
OUTPUT
2Разбор примера
get захватывает глобальную ячейку x, а не замороженную копию числа 1. set! записывает 2 в ту же ячейку, поэтому следующий вызов видит 2. Новая внутренняя x лишь затенила бы внешнюю.
Как рассуждать
- Поиск выбирает ближайшую лексическую ячейку; затенение не изменяет внешнее связывание.
letrecвыделяет ячейки до инициализаторов и выдаёт E321 при чтении неинициализированной ячейки.- Повторный верхнеуровневый
defineобновляет существующую глобальную ячейку, сохраняя ссылки старых замыканий.
Быстрый выбор
| Форма | Назначение | Где вычисляются инициализаторы |
|---|---|---|
define | верхнеуровневое имя или именованная процедура | текущая верхняя область |
let | независимые локальные значения | внешняя область |
let* | локальные значения с зависимостями по порядку | все предыдущие связывания |
letrec | взаимно рекурсивные локальные процедуры | все выделенные ячейки; раннее чтение ошибочно |
set! | изменение существующей ячейки | новое связывание не создаётся |
Частая ошибка
set! для несвязанного имени даёт E303 и не создаёт связывание.
Текущие границы
- Внутренний маркер неинициализированности нельзя представить гостевым значением.
Куда дальше
В главе о процедурах показан захват ячеек, а руководство по замыканиям превращает это правило в небольшой пример с состоянием.