Проверьте запуском
LISPEX
(define counter (let ((n 0)) (lambda () (set! n (+ n 1)) n)))
(list (counter) (counter))Наблюдаемый результат
OUTPUT
(1 2)Разберите приём
- Создайте закрытую ячейку. Внешний
letодин раз выделяетnпри созданииcounter. - Верните поведение, а не ячейку. После завершения
letтолько lambda имеет доступ к этому связыванию. - Наблюдайте общие изменения. Оба вызова используют одно замыкание, поэтому второй видит
set!первого.
Как рассуждать
- Создайте состояние во внешнем
let, верните процедуры чтения/изменения и оставьте ячейку доступной только через них. - Затенённые связывания получают другие ячейки;
set!обновляет ближайшее разрешённое связывание. - Верхнеуровневое переопределение обновляет исходную глобальную ячейку, видимую старым замыканиям.
Проверьте себя
Если дважды вычислить внешний let и создать counter-a и counter-b, изменит ли вызов первого состояние второго?
Ответ
Нет. Каждое вычисление выделяет отдельную лексическую ячейку. Состояние общее лишь у замыканий, захвативших одну ячейку.
Частая ошибка
Общее изменение здесь детерминировано, но всё равно требует узкого владения и явных тестов.
Куда дальше
Точные правила ячеек и затенения находятся в связываниях. Если состояние можно сделать явным входом, руководство по решениям будет проще.