Читайте синтаксис, передавайте контекст и делайте управление явным.
Двадцать три канонические программы уроков повторно связывают данные выражений, лексические окружения, записи вычислителя, трансформацию исходного кода, анализируемое выполнение, строгие и ленивые стратегии, недетерминированные continuation, потоки кадров, унификацию, поиск по правилам, а также точные границы входных данных и ресурсов каждого конечного наблюдения.
Можете ли вы объяснить, как одна представленная программа проходит через классификацию синтаксиса, поиск в окружении, применение процедур, отложенный запрос, альтернативные continuation и расширение кадров запроса, не путая учебную модель со встроенной средой выполнения Lispex?
- Интерпретировать дерево цитированных выражений как данные
- Связывать один и тот же символ по-разному в разных окружениях
- Вычислять только выбранную ветвь специальной формы
- Анализировать структуру выражения один раз и повторно использовать план выполнения
- Трансформировать let в существующее правило применения lambda
- Выполнить force явного thunk один раз и мемоизировать его значение
- Сохранять каждую успешную конечную альтернативу
- Расширять кадр только согласованными связываниями образца
- Передавать кадры через конечную последовательность целей правил
- Сообщать о завершении или усечении рекурсивного поиска с видимой границей frontier
- Отличать неудачный поиск от отрицания, а декларативное отношение от процедурного поиска
- Выделять внутренние имена до того, как присваивания установят взаимно рекурсивные значения процедур
- Выполнять цитированные формы верхнего уровня через eval/apply, сохраняя определения в едином глобальном окружении
- Передавать continuation успеха и неудачи, чтобы require мог возобновить более ранний выбор amb
- Разделять диспетчеризацию синтаксиса в eval и диспетчеризацию процедур в apply
- Конструировать кадры, цепочки окружений, записи примитивов, составные процедуры и упорядоченные списки аргументов
- Генерировать, инспектировать, трансформировать и затем вычислять данные программ
- Сравнивать аппликативный порядок с немемоизированным нормальным порядком вычислений
- Откладывать аргументы составных процедур и мемоизировать запрошенные значения в ленивом вычислителе
- Создавать неограниченный ленивый список на гостевом языке guest и запрашивать конечный префикс
- Выражать конечные головоломки как выборы с ограничениями и анализировать порядок решений
- Рассматривать запрос как преобразование из входного потока кадров в выходной поток кадров
- Переименовывать переменные правил, унифицировать термы и применять видимый бюджет глубины правил
Первые уроки устанавливают границы представления. Выражения, окружения, записи процедур и преобразованный исходный код представляют собой обычные данные, передаваемые в конечные учебные вычислители. Каждый урок явно определяет синтаксис и структуры данных в своей модели.
Собранные уроки по eval/apply связывают эти части: eval классифицирует исходные формы, apply отличает примитивные процедуры от составных, кадры содержат изменяемые связывания, замыкания сохраняют лексические окружения, а конечный драйвер сохраняет единое глобальное окружение guest между формами верхнего уровня.
Последовательность уроков о порядке вычислений изменяет протокол управления. Строгий порядок вычисляет операнды перед применением. Модель нормального порядка откладывает их без мемоизации. Ленивый вычислитель сохраняет thunk с выражением и окружением и заменяет запрошенный thunk его значением, а урок по ленивым спискам определяет обычные процедуры guest, которые ведут себя как нестрогие конструкторы списков.
Недетерминированная последовательность уроков передает continuation успеха и неудачи. Форма amb помещает оставшиеся варианты в путь неудачи, require вызывает этот путь при невыполнении ограничения, а урок с головоломками использует тот же протокол вычислителя для перечисления конечных решений в явном порядке.
Последовательность уроков логического программирования передает кадры через утверждения, конъюнкции, дизъюнкции и правила. Урок по реализации добавляет двустороннюю унификацию и новые имена переменных правил, а явные ограничения на объем работы и глубину не позволяют незавершенному или циклическому поиску выглядеть как полный ответ.
Для всех двадцати трех программ трасса выполнения с фиксированными лимитами регистрирует точные события одного выбранного запуска в учебных системах вычислителя и запросов.
- Вывод
- —
- Значение
- —
- Диагностика
- —
Каждая программа возвращает первое ожидаемое наблюдение из соответствующего урока главы 4. Конечный драйвер возвращает (complete ((defined square) (defined make-adder) (defined add-five) (defined base) 49 12 (assigned base) 81 6) 0), а вычислитель amb возвращает (complete ((1 4) (2 3))) для поиска пар с ограничениями. Новые уроки дополнительно раскрывают записи вычислителя, сгенерированные данные программ, подсчет строгой и отложенной работы, мемоизированные запросы, конечные префиксы ленивых списков, упорядоченные решения головоломок, преобразования потоков кадров и раскрытие правил в рамках бюджета работы.
Проследите за обходом выражений, поиском в ближайшем кадре, выбором ветви, построением анализа, трансформацией исходного кода, применением примитивных и составных процедур, порядком списка аргументов, созданием и мемоизацией thunk, запросом элементов конечного ленивого списка, установкой вариантов amb, неудачей require, возвратом в головоломках, расширением кадров, сканированием утверждений, последовательностью конъюнкций, переименованием переменных правил, унификацией и уменьшением бюджета. Эти наблюдения относятся только к выбранным конечным запускам.
Двадцать три вопроса для проверки вычислителей, отложенной работы, альтернатив и запросов
Что должен сделать вычислитель, чтобы превратить данные выражения в значение?
Ответ Числа вычисляются напрямую. Составное выражение требует от evaluate интерпретировать оба вложенных операнда перед объединением их значений. Вычислитель обходит дерево выражения по одному узлу за раз.
Почему одно и то же выражение может давать другое значение в другом окружении?
Ответ evaluate не связывает постоянное значение с x или y. Процедура получает окружение вместе с выражением, поэтому одно и то же дерево выражения можно использовать повторно с другими связываниями.
Что пойдет не так, если вычислитель вычислит обе ветви if?
Ответ В первом примере альтернатива делит на ноль. Программа все равно возвращает 60, так как истинный предикат выбирает ветвь сложения, а некорректная альтернатива остается данными выражения.
Какую работу можно выполнить до того, как выражение получит окружение?
Ответ Полученный plan принимает окружение. Его запуск выполняет только поиск переменных, запуск сохраненных планов операндов и применение уже выбранного оператора. Поэтому один проанализированный plan может обслуживать множество окружений.
Что должно оставаться неизменным при переписывании let в виде применения lambda?
Ответ Вычислителю не требуется вторая реализация локального связывания. Его случай let переписывает выражение и отправляет результат обратно через обычные случаи lambda и применения в том же окружении.
Какие изменения представления делают call-by-need видимым внутри вычислителя?
Ответ force-it проверяет тег. При первом запросе процедура вызывает сохраненное вычисление, меняет тег на evaluated-thunk, удаляет вычисление и сохраняет значение. Последующие запросы выбирают кэшированный слот и не выполняют вычисление повторно. Это раскрывает изменение представления, которое ленивый вычислитель обычно скрывает за обработкой аргументов.
Как меняется вычисление, если после одного успеха сохраняются оставшиеся альтернативы?
Ответ Вторая программа делает явными две позиции выбора. scan-y проверяет каждый y для одного x, а scan-x повторяет эту работу для каждого x. Возврат всех пар, сумма квадратов компонентов которых равна 25, раскрывает конечный недетерминированный поиск как обычное управление, создающее списки.
Как сопоставитель может переносить частичное знание при обходе двух структур данных?
Ответ При последующем появлении выполняется поиск существующего связывания перед любым расширением. Одинаковые данные сохраняют кадр, а конфликтующие данные возвращают failed, что распространяется на все оставшиеся рекурсивные шаги. Это одностороннее сопоставление с переменными в образце, а не полная двунаправленная унификация или поиск по базе данных.
Как правило сохраняет промежуточное связывание, необходимое для его следующей цели?
Ответ Вторая программа связывает grand из заголовка правила, затем solve-goals обрабатывает две цели parent по порядку. Первая цель дает промежуточные значения ben и dia. Каждый кадр становится входными данными для второй цели, которая находит cy и eli. Вычислитель урока обрабатывает прямые факты parent и одно упорядоченное тело правила parent, сохраняя каждый промежуточный кадр.
Как рекурсивное раскрытие правил может корректно завершаться, если данные могут содержать цикл?
Ответ Вторая программа использует цикл из трех человек. Раскрытие ada достигает ben, ben достигает cy, а cy снова достигает ada. Вычислитель тратит ровно одну единицу работы на каждый извлеченный элемент frontier и возвращает truncated с оставшимся frontier, когда бюджет достигает нуля. Эта модель урока для поиска в ширину охватывает раскрытие parent, явный frontier, повторяющиеся ответы и статус complete или truncated.
Какие выводы следуют из логического отношения, а какие определяются конкретной базой данных и процедурой поиска?
Ответ Вторая программа задает для married симметричное операционное правило: если прямой факт отсутствует, поменять аргументы местами и выполнить поиск снова. Это доказывает (married mickey minnie) после одной перестановки, поскольку существует обратный факт. То же самое правило бесконечно чередуется для несвязанной пары, если только вычислитель не обнаружит повторение или не исчерпает видимый бюджет работы. Логическое утверждение может быть симметричным, но направление и управление правила все равно определяют поведение этого исполняемого поиска.
Почему вычислитель должен создать все внутренние связывания до того, как установит какие-либо значения процедур?
Ответ Вторая программа выполняет обе формы. classify-original использует внутренние определения напрямую. classify-scanned явно записывает преобразованную структуру let и set!. Обе формы возвращают одинаковые результаты проверки четности для 7 и 8, и equal? возвращает true. Конечное сравнение проверяет это преобразование и делает видимой его явную промежуточную структуру.
Что превращает набор процедур вычислителя в программу, способную выполнять последовательность пользовательских форм?
Ответ Процедура run-program служит драйвером. Она принимает цитированные формы верхнего уровня, одно явное глобальное окружение и бюджет форм. Каждая завершенная форма добавляет одно значение в transcript, при этом определения и присваивания остаются видимыми для последующих форм. Полный запуск определяет square, make-adder, add-five и base, а add-five сохраняет локальное значение x равным 5 даже после изменения глобальной переменной base. Второй запуск останавливается после пяти форм и сообщает, что четыре формы еще ожидают обработки. Этот бюджет форм не ограничивает рекурсию или работу внутри одной формы, а ограничения выполнения более низкого уровня по-прежнему обеспечивает упакованная среда выполнения браузера.
Как вычислитель может превратить неудачу из фатальной ошибки в запрос на возобновление работы с предыдущей точки выбора?
Ответ Форма require вычисляет свой предикат в той же системе продолжений. Истинный предикат завершается успехом со значением ok, а ложный предикат вызывает следующую альтернативу предиката, которая в итоге возвращается к самому последнему выбору amb. Процедура all-values повторно вызывает следующую альтернативу, предоставляемую каждым успехом. Первый запуск исчерпывает выбранный конечный поиск и сообщает complete. Второй запуск намеренно останавливается после четырех решений и сообщает truncated. Учебный вычислитель охватывает упорядоченные варианты amb, форму require, применение процедур и передачу продолжений успеха или неудачи.
Какие обязанности принадлежат eval, какие принадлежат apply и какие структуры данных связывают их между собой?
Ответ Процедура apply-procedure принимает уже классифицированные значения процедур. Процедура хоста получает список аргументов через apply. Запись compound-procedure предоставляет параметры, выражения тела и окружение своего создания, а применение расширяет это окружение новым кадром и вычисляет последовательность тела. Повторно используемый канонический драйвер выполняет каждую базовую форму в одном постоянном окружении guest и делает наглядным учебный цикл eval/apply.
Какую информацию должен сохранять вычислитель, чтобы процедуру можно было применить позже в том окружении, где она была создана?
Ответ Вторая программа помечает процедуру хоста + тегом данных primitive и применяет ее после извлечения реализации. Процедура list-of-values фиксирует вычисление операндов слева направо с помощью явных зондов. Предикат истинности guest возвращает ложь только для #f, поэтому ноль и цитированный символ считаются истиной. Вместе эти записи определяют протокол учебного вычислителя для кадров, процедур, аргументов и проверок истинности.
Что становится возможным, когда вычислитель принимает те же списочные структуры, которые обычные процедуры могут конструировать и преобразовывать?
Ответ Вторая программа рассматривает квадратное выражение как данные и рекурсивно заменяет символ x на 3. Преобразование сохраняет арифметику как символьные данные. Затем arithmetic-eval интерпретирует + и * и дает 16. Учебный вычислитель обрабатывает перечисленные арифметические формы через то же явное представление, общее для генератора и преобразователя.
Как два вычислителя могут возвращать одно и то же значение, выполняя при этом разный объем работы с аргументами?
Ответ Во втором выражении тело использует x дважды. Строгое вычисление выполняет probe один раз и связывает полученное значение 10. Немемоизированный вычислитель нормального порядка форсирует сохраненное выражение при каждом поиске, поэтому probe запускается дважды. Оба возвращают 20, но объем работы различается. Эти два вычислителя представляют собой явные модели guest, и они не меняют порядок вычислений выполняющей их программы хоста Lispex.
Как вычислитель может откладывать работу с аргументами, не допуская повторного вычисления одного и того же выражения при многократных обращениях?
Ответ force-it вычисляет свежий thunk один раз, перезаписывает ту же изменяемую запись в evaluated-thunk, сохраняет результат и отбрасывает сохраненные выражение и окружение. Неиспользованный аргумент не выполняет вызов probe. Продублированное выражение x запрашивается дважды, но вызывает probe один раз. Невыбранная ветвь формы if не вычисляется. Этот вычислитель реализует вызов по требованию для перечисленных форм в рамках явного бюджета работы.
Что происходит с абстракцией списков, когда вычислитель автоматически откладывает каждый операнд составной процедуры?
Ответ integers-from и lazy-map используют тот же обычный рекурсивный синтаксис. Их рекурсивные вызовы передаются как отложенные операнды y в lazy-cons, поэтому бесконечный остаток представлен мемоизированными thunk вычислителя. take служит границей требования: примитивному cons нужны фактические значения аргументов, поэтому он форсирует ровно запрошенное число голов и хвостов в конечный базовый список. Этот пример объединяет ленивые списки с вычислителем урока и делает требование потребителя явным.
Как программа может описывать допустимые ответы, когда протокол продолжений управляет порядком вариантов и поиском с возвратом?
Ответ Программа для пифагоровых троек повторно использует те же продолжения для числовых выборов. Ограничения упорядочения исключают перестановки, а равенство квадратов принимает две тройки в конечном диапазоне от 1 до 10. Оба примера используют конечные списки с поиском в глубину и фиксируют каждый выбор, достигнутый в этом диапазоне.
Как один запрос сохраняет возможные связывания переменных, пока последующий запрос сужает эти возможности?
Ответ qeval распознает and как конвейер. Первый запрос создает возможные связывания who для всех программистов. Второй запрос получает каждый кадр отдельно и сохраняет только тот кадр, где тот же who также руководит под началом bob. Этот урок материализует конечный поток кадров в виде списка, сохраняя протокол конвейера.
Какие дополнительные механизмы позволяют сопоставлять запрос не только с сохраненными фактами, но и с выводами, полученными из переиспользуемых правил?
Ответ simple-query объединяет прямые совпадения с утверждениями и результаты правил. Результат правила унифицирует шаблон запроса с переименованным заключением, затем передает полученный кадр через qeval в тело правила. and передает кадры по конвейеру, or объединяет альтернативы, а not сохраняет кадр только тогда, когда его подзапрос не дает результата в этом кадре. Явная глубина служит точной конечной границей ресурсов для раскрытия правил.