sicp.io
4.2.2 · Интерпретатор с ленивым вычислением

Ленивый вычислитель и мемоизация thunk

Операнды составных процедур откладываются, примитивные операнды и предикаты форсируются, а thunk после первого actual-value становится evaluated-thunk.

Вопрос для размышления

Как вычислитель может откладывать работу с аргументами, не допуская повторного вычисления одного и того же выражения при многократных обращениях?

  • Откладывание выражений операндов вместе с их вызывающими окружениями
  • Форсирование оператора перед выбором способа его применения
  • Форсирование каждого примитивного аргумента и предиката if до фактического значения
  • Передача отложенных аргументов составным процедурам
  • Мемоизация thunk путем изменения тега и сохраненной полезной нагрузки
  • Раздельное наблюдение за неиспользованными, повторяющимися и невыбранными выражениями

apply-lazy отличает базовые примитивы от представленных составных процедур. Примитиву требуются фактические значения аргументов, поэтому его операнды форсируются перед вызовом host apply. Составная процедура вместо этого получает записи thunk, содержащие исходные выражения операндов и вызывающее окружение. Поиск переменной возвращает эту запись, а actual-value определяет необходимость ее форсирования.

force-it вычисляет свежий thunk один раз, перезаписывает ту же изменяемую запись в evaluated-thunk, сохраняет результат и отбрасывает сохраненные выражение и окружение. Неиспользованный аргумент не выполняет вызов probe. Продублированное выражение x запрашивается дважды, но вызывает probe один раз. Невыбранная ветвь формы if не вычисляется. Этот вычислитель реализует вызов по требованию для перечисленных форм в рамках явного бюджета работы.

Код SICP7,892 из 1,048,576 байт UTF-8
Примеры
Результат
Вывод
Значение
Диагностика
Трасса выполнения0 / 0 событий
    Запуски происходят внутри браузера с отображением результата программы и трассы выполнения.
    Ожидаемый результат

    Вычислитель возвращает ((unused 1 0) (duplicated 20 1) (branch safe 0)).

    На что обратить внимание в трассе

    Проследите применение составной процедуры до list-of-delayed-args, а затем найдите первое обращение, отправляющее thunk в force-it. Убедитесь в изменении записи на evaluated-thunk и проверьте, что второе обращение возвращает кэшированное значение. В выражении if подтвердите, что вычисляются только предикат и выбранная ветвь consequent.

    Попробуйте сами

    Измените программу и сравните результат.

    Добавьте ((lambda (x) (+ x (+ x x))) (probe 4)) и спрогнозируйте значение и число вызовов probe. Затем удалите мутацию thunk в force-it и сравните результат.

    Показать подсказку

    При мемоизации аргумент вызывает probe один раз независимо от числа обращений к x. Без мутации каждое обращение заново вычисляет сохраненное выражение.

    Завершить этот урок

    В этой главе пройдено 0 из 23 урока0%