Песочница

Связывания, область, ячейки и изменение

Каждое лексическое связывание является изменяемой ячейкой. Замыкания захватывают кадры и видят последующие изменения той же ячейки через set!.

Когда это нужно

Связывание даёт результату имя, создаёт локальную область или намеренно сохраняет состояние между вызовами. Обычно достаточно define и let. Используйте set!, только если совместное изменение входит в модель.

Проверьте запуском

LISPEX
(define x 1)
(define get (lambda () x))
(set! x 2)
(get)

Наблюдаемый результат

OUTPUT
2

Разбор примера

get захватывает глобальную ячейку x, а не замороженную копию числа 1. set! записывает 2 в ту же ячейку, поэтому следующий вызов видит 2. Новая внутренняя x лишь затенила бы внешнюю.

let* создаёт имена по очереди сверху вниз, поэтому следующее начальное выражение видит только что созданное имя.

LISPEX
(let* ((x 6)
       (y (+ x 1)))
  (* x y))

Наблюдаемый результат

OUTPUT
42

letrec выделяет все ячейки до вычисления начальных выражений, и именно поэтому имя может вызвать само себя.

LISPEX
(letrec ((f (lambda (n)
              (if (= n 0)
                  1
                  (* n (f (- n 1)))))))
  (f 5))

Наблюдаемый результат

OUTPUT
120

Как рассуждать

  • Поиск выбирает ближайшую лексическую ячейку, а затенение не изменяет внешнее связывание.
  • letrec выделяет ячейки до инициализаторов и выдаёт E321 при чтении неинициализированной ячейки.
  • Повторный верхнеуровневый define обновляет существующую глобальную ячейку, сохраняя ссылки старых замыканий.

Быстрый выбор

ФормаНазначениеГде вычисляются инициализаторы
defineверхнеуровневое имя или именованная процедуратекущая верхняя область
letнезависимые локальные значениявнешняя область
let*локальные значения с зависимостями по порядкувсе предыдущие связывания
letrecвзаимно рекурсивные локальные процедурывсе выделенные ячейки, но раннее чтение ошибочно
set!изменение существующей ячейкиновое связывание не создаётся

Частая ошибка

set! для несвязанного имени даёт E303 и не создаёт связывание.

Текущие границы

  • Внутренний маркер неинициализированной ячейки нельзя представить гостевым значением.

Куда дальше

В главе о процедурах показан захват ячеек, а руководство по замыканиям превращает это правило в небольшой пример с состоянием.

Процедуры · Руководство по замыканиям