Разрешите имя. Выберите путь. Сохраните границу.
Используйте семнадцать исполняемых программ, чтобы связать воедино все восемнадцать уроков Главы 1. Сначала спрогнозируйте значения и пути, определите ключевое связывание или границу абстракции, а затем объясните, что видимый процесс добавляет к окончательному результату.
Можете ли вы объяснить, какое окружение предоставляет имя, какой предикат выбирает действие, что процедура сохраняет скрытым, как lambda формирует поведение, что подтверждает проверка простоты и как растет итоговый процесс?
- Читать оператор и его операнды как единую комбинацию
- Объяснять, как определения, параметры и связывания let разрешают имя
- Конструировать пути подстановки аппликативного и нормального порядков в виде данных
- Прослеживать пути cond, if и предикатов короткого замыкания
- Отделять публичный контракт процедуры от локальных вспомогательных связываний
- Объяснять, какое окружение вызова предоставляет параметр процедуры
- Сопоставлять формы рекурсивных и итеративных процессов
- Прослеживать процедуру, переданную в качестве аргумента
- Создавать и применять анонимные процедуры с лексической областью
- Строить новые преобразования путем возврата и композиции процедур
- Сохранять инвариант при редукции задачи
- Уточнять числовое приближение через фиксированный итеративный процесс
- Накапливать конечную цепную дробь начиная с ее последнего члена
- Искать неподвижную точку, передавая каждое преобразованное значение на следующий шаг
- Уменьшать показатель степени, сохраняя инвариант произведения
- Вычислять степени по модулю и ограничивать выводы теста Ферма с фиксированным основанием
- Различать доказательства методом пробного деления и свидетельства вероятной простоты
- Сравнивать рост вызовов древовидной рекурсии с линейными переходами состояний итерации
Начните с имен и подстановки. Пример с окружением разделяет глобальные связывания, параметры и локальные связывания let. Данные подстановки затем сопоставляют редукцию операнда до подстановки с предварительной подстановкой выражения, и эти списки объясняют простое применение, а не раскрывают скрытые кадры Lispex.
Затем проследите управление. Форма cond выбирает первое совпавшее предложение, if защищает от деления на ноль, а формы короткого замыкания оставляют недостижимые вызовы record! отсутствующими. Только #f является ложью, поэтому цитированный символ может выбрать истинную ветвь.
Пример черного ящика оставляет choose и square локальными, сохраняя единый публичный контракт процедуры. Две реализации используют различное внутреннее разбиение, тогда как вызывающий код зависит только от входных данных и результата. Совпадающие конечные примеры подтверждают общий результат для показанных входных данных.
Сравните два процесса вычисления факториала, а затем проследите процедуры как значения. sum принимает term и next, вложенная lambda находит связывания лексически, а compose строит процедуру, захваченное поведение которой выполняется только при последующем применении.
Алгоритм Евклида сохраняет наибольший общий делитель, уменьшая пару. Улучшение квадратного корня, цепные дроби и неподвижные точки переносят каждое свое полное следующее состояние. Быстрое возведение в степень и expmod сохраняют явные числовые соотношения при уменьшении оставшейся задачи.
Наконец, сравните процедуры проверки простоты. Пробное деление подтверждает показанные малые кандидаты путем исчерпания границы квадратного корня, тогда как выбранные основания Ферма все еще могут принять число Кармайкла 561. Затем сопоставьте повторяющееся дерево вызовов Фибоначчи с итеративным состоянием фиксированного размера.
- Вывод
- —
- Значение
- —
- Диагностика
- —
Программа окружения возвращает ((global-x 10) (parameter-x 3 local-y 4 sum 7) (global-after 10)). Программа подстановки возвращает списки редукции аппликативного и нормального порядков, заканчивающиеся на 25. Программа условных выражений возвращает (negative zero small-positive large-positive undefined 5 truthy). Программа черного ящика возвращает (13 61 global-marker). Обе программы факториала возвращают 40320. Сумма высшего порядка возвращает 55. Программа с вложенной lambda возвращает (100 7 100). Композиция возвращаемых процедур возвращает (49 37 15). Алгоритм Евклида возвращает 2. Программы квадратного корня, цепных дробей, неподвижной точки, возведения в степень и вычислений по модулю сохраняют результаты своих уроков. Сравнение проверок простоты возвращает ((prime-7 #t #t) (composite-15 #f #f) (carmichael-561 #f #t)). Измерение Фибоначчи возвращает (21 67 8).
Найдите ближайшее связывание в окружении, затем отличите цитированные данные подстановки от фактических применений. Проследите каждую выбранную ветвь предиката и убедитесь в отсутствии пропущенных вызовов. Входите в локальные вспомогательные процедуры черного ящика и выходите из них, не путая их с глобальными именами. Отделяйте создание lambda от применения, прослеживайте процедурные аргументы и возвращаемые процедуры, затем проверьте алгоритм Евклида, численное улучшение, уменьшение показателя степени, редукцию по модулю, пробные делители, выбранные основания Ферма и повторные вызовы Фибоначчи. Каждая трасса фиксирует именно выбранный конечный запуск в рамках объявленных ограничений среды выполнения.
Семнадцать коротких вопросов для проверки собственного понимания
Как определение процедуры превращает выражение в повторно используемый метод?
Ответ При применении каждый параметр связывается со значением аргумента в новом окружении. Тело вычисляется с этими связываниями, давая 25 для входных значений 3 и 4.
Чем рекурсивный процесс отличается от итеративного?
Ответ Оба определения являются рекурсивными процедурами, поскольку каждое из них вызывает само себя. Только второе порождает итеративный процесс, состояние которого можно описать фиксированным числом переменных.
Как одна процедура может описать целое семейство сумм?
Ответ Такое разделение служит началом мощного навыка проектирования. Назовите неизменный процесс один раз и передавайте изменяющиеся решения.
Почему замена a и b на b и остаток сохраняет их наибольший общий делитель?
Ответ Каждый вызов заменяет (a, b) на (b, remainder(a, b)). Когда второе значение достигает нуля, первое значение является сохраненным наибольшим общим делителем. После рекурсивного вызова не остается отложенных арифметических действий.
Как одно локальное правило улучшения создает все более точный численный процесс?
Ответ Эти программы используют фиксированное число уточнений вместо скрытой погрешности. Шесть шагов от 1.0 дают детерминированное наблюдение для √2, а явная история показывает, как изменение между приближениями быстро уменьшается.
Как порядок вычисления меняет процесс, не изменяя значение конечной дроби?
Ответ Итеративная версия начинается с члена k со значением 0.0 в качестве уже вычисленного хвоста. Каждый вызов заменяет result одним полным слоем дроби и движется к члену 1. В обоих примерах используются десять числителей и знаменателей, равных 1.0, поэтому они возвращают одинаковое конечное приближение к величине, обратной золотому сечению.
Что означает поиск значения, которое преобразование оставляет неизменным?
Ответ fixed-point не скрывает решение об остановке внутри ненаблюдаемой погрешности. Она принимает преобразование, текущее приближение и оставшееся число шагов как свое полное состояние. Версия с историей записывает ту же передачу данных, поэтому чередующиеся приближения остаются видимыми.
Как можно быстро уменьшать показатель степени, не меняя вычисляемую степень?
Ответ Процесс останавливается, когда exponent достигает нуля, поскольку оставшаяся степень равна 1, а product уже содержит ответ. Программа с историей записывает полное состояние при каждом вызове, поэтому каждый четный и нечетный переход можно проверить напрямую.
Что одно сравнение по модулю может установить для числа-кандидата, а чего оно установить не может?
Ответ passes-base-2? проверяет, сравнимо ли число 2 в степени candidate с 2 по модулю candidate. Проверка проходит для 17 и не проходит для 15. Она также проходит для 561, хотя 561 равняется 3, умноженному на 11 и на 17. Каждый результат точно фиксирует сравнение по основанию 2, в то время как пробное деление дает классификацию составного числа.
Как две процедуры могут возвращать одно и то же значение Фибоначчи, когда их потребности во времени и памяти растут по-разному?
Ответ Для n, равного 8, инструментальное дерево возвращает 21 после 67 применений процедуры и достигает глубины 8. Итеративный процесс переносит два последовательных значения Фибоначчи вместе со счетчиком remaining и достигает того же значения за восемь переходов. Для этой хрестоматийной пары время наивной рекурсии растет экспоненциально, тогда как глубина растет линейно, а время итерации растет линейно, тогда как число переменных состояния остается постоянным. Отображаемые счетчики точно фиксируют выбранные программы и входные данные.
Что становится возможным, когда результатом вызова процедуры является сама процедура?
Ответ average-damp также возвращает процедуру. Возвращенное преобразование вычисляет исходную f для x и находит среднее значение этого результата и x. Для преобразования квадратного корня y ↦ 16/y приближение 2 становится 5, тогда как неподвижная точка 4 остается 4. Конструктор процедур содержит повторно используемый метод, а переданная f определяет преобразуемое поведение.
Как вычислитель решает, какое значение обозначает имя, когда сосуществуют глобальные определения, параметры и локальные имена?
Ответ Вызов describe создает новое окружение, где параметр x связан со значением 3, даже если в окружающем окружении x уже связан со значением 10. Внутренний let добавляет y в это окружение вызова. Поиск связываний в теле сначала находит ближайшее совпадающее связывание, поэтому x обозначает 3 внутри вызова и 10 снаружи до и после него. Локальный y исчезает, когда вызов завершается.
Что подстановка показывает в применении процедур и что меняется, когда аргументы вычисляются до или после их подстановки в тело?
Ответ Вторая программа делает разницу в объеме работы наглядной с помощью процедуры argument, которая увеличивает счетчик. Обычный вызов Lispex вида (square (argument)) вычисляет аргумент один раз перед применением square. Явный план normal-model вызывает argument дважды, потому что тело использует x дважды. Оба плана возвращают 25, но количество их вызовов различается. Это сравнение фиксирует два явно написанных конечных плана и точное количество их вызовов.
Как программа может принимать решение без вычисления работы, относящейся к каждой возможной ветви?
Ответ Формы and и or являются управляющими формами, а не обычными процедурами. and останавливается на первом ложном значении, а or останавливается на первом значении, отличном от #f. Вторая программа фиксирует только те операнды, до которых фактически дошло выполнение, показывая пропущенные выражения через их отсутствие. Этот конечный запуск демонстрирует выбранный путь, а не все возможные стратегии оптимизатора или реализации.
Что может измениться внутри процедуры без необходимости менять каждый вызывающий ее код?
Ответ Вторая программа вычисляет один и тот же результат двумя разными вариантами декомпозиции: прямой выбор двух наибольших значений или вычитание квадрата наименьшего значения из суммы квадратов всех трех чисел. Публичный вопрос не меняется, даже если внутренняя работа различается. Совпадение подтверждает общий результат для отображаемых конечных входных данных.
Что устанавливает каждая процедура проверки простоты и что все еще может оставаться неопределенным после того, как она возвращает true?
Ответ Программа Ферма проверяет, сравнимо ли a^n с a по модулю n для выбранного списка оснований. Простое число 7 проходит проверку, составное 15 быстро дает сбой, а составное 561 проходит проверку для трех взаимно простых оснований, хотя метод пробных делителей находит множитель. Отображаемый результат фиксирует решение о вероятной простоте в рамках выбранной процедуры Ферма.
Какое значение создает выражение lambda и какие связывания будет использовать его тело при последующем применении этого значения?
Ответ Во вложенном примере внешняя lambda связывает x со значением 3, а внутренняя lambda связывает y со значением 4. Внутреннее тело находит y в собственном вызове, а x в окружающем лексическом окружении, при этом глобальный x остается равным 100. Последнее выражение передает вновь созданную процедуру square в другую lambda, показывая, что создание, связывание, передача и применение процедур используют обычную модель значений.