sicp.io
4.1.9 · Вынесение внутренних определений

Явная локальная инициализация внутренних имен

Тело процедуры сначала размещает каждое внутреннее имя, затем задает значения присваиванием, поэтому взаимно рекурсивные процедуры делят одно окружение.

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

Почему вычислитель должен создать все внутренние связывания до того, как установит какие-либо значения процедур?

  • Распознавание начальных внутренних определений в теле процедуры
  • Преобразование синтаксиса определения процедуры в явное значение lambda
  • Размещение каждого локального имени с маркером unassigned перед присваиванием
  • Сохранение исходного тела без определений после присваиваний
  • Сравнение исходной и преобразованной программ на одинаковых конечных входных данных
  • Проверка согласованности для показанного преобразования и входных данных

Проход scan-out считывает тело процедуры как данные. Он берет начальные формы define, преобразует сокращенную запись определения процедуры в выражения lambda, создает одно связывание let для каждого имени с явным маркером unassigned и помещает set! для каждого определения перед исходным оставшимся телом. Таким образом, имена существуют вместе до применения любого замыкания, что дает взаимно рекурсивным вспомогательным процедурам одно общее локальное окружение.

Вторая программа выполняет обе формы. classify-original использует внутренние определения напрямую. classify-scanned явно записывает преобразованную структуру let и set!. Обе формы возвращают одинаковые результаты проверки четности для 7 и 8, и equal? возвращает true. Конечное сравнение проверяет это преобразование и делает видимой его явную промежуточную структуру.

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

    Программа преобразования возвращает одно выражение let с even-step и odd-step, связанными с цитированными маркерами *unassigned*, за которыми следуют две формы set! и исходное тело списка. Программа сравнения возвращает (#t ((#f #t) (#t #f)) ((#f #t) (#t #f))).

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

    В запуске преобразования отделите чтение цитированных данных исходного кода от сбора начальных определений, построения связываний, преобразования определений процедур в значения lambda, построения присваиваний и присоединения оставшегося тела. В запуске сравнения найдите одно окружение let, обе операции set! и чередующиеся вызовы замыканий. Согласованность сообщается только для выбранных входных данных.

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

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

    Добавьте третью внутреннюю вспомогательную процедуру, которая распознает значения, делящиеся на три, с помощью трех взаимно рекурсивных процедур для остатка. Напишите как исходную, так и явно преобразованную версию scan-out, затем сравните их результаты для 8 и 9.

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

    Разместите все три имени перед присваиваниями. Каждое замыкание вызывает следующую процедуру остатка после вычитания единицы, и только процедура нулевого остатка возвращает true при нуле.

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

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