sicp.io
Глава 2 · Проверка

Следуйте интерфейсу. Сохраняйте инвариант. Расширяйте таблицу.

Используйте двадцать исполняемых программ, чтобы связать воедино каждый урок Главы 2: барьеры абстракции, процедурные данные, последовательности, деревья, идентичность, символьную структуру, множества, обобщенную диспетчеризацию, приведение и понижение типов, разреженную алгебру, рисователи, кадры и рекурсивную композицию изображений.

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

Можете ли вы объяснить программу обработки данных, не смешивая ее абстрактный контракт, инвариант представления, идентичность, символьную структуру, зарегистрированные методы, путь преобразования и геометрический кадр?

  • Скрывать знание о представлении за конструкторами и селекторами
  • Распознавать процедурные представления данных, сохраняющие поведение
  • Прослеживать последовательность шаг за шагом по cdr
  • Прослеживать обе ветви дерева, не делая предположений о его глубине
  • Отличать один разделяемый объект от двух равных объектов
  • Комбинировать перечисление, фильтрацию, отображение и накопление
  • Преобразовывать данные символьных выражений с помощью упрощающих конструкторов
  • Различать цитирование, идентичность и рекурсивное структурное равенство
  • Диспетчеризировать обобщенную операцию с помощью тега типа данных
  • Распространять нижнюю и верхнюю границы через интервальный интерфейс
  • Сравнивать неупорядоченные, упорядоченные и древовидные представления множеств
  • Использовать представление дерева кодов для кодирования и декодирования путей ветвления
  • Выбирать декартово или полярное представление за единым интерфейсом комплексных чисел
  • Преобразовывать смешанные числовые значения через явное приведение coercion или пути повышения типа
  • Складывать и умножать разреженные многочлены с помощью обобщенных тегированных методов
  • Отображать рисователь единичного квадрата на квадратные или скошенные кадры
  • Компоновать преобразованные рисователи и получать конечный рекурсивный набор отрезков
  • Устанавливать новое представление без изменения обобщенного диспетчера
  • Повышать значения типов до общего арифметического метода
  • Понижать результат только тогда, когда проекция и повторное повышение типа сохраняют его информацию

Программы рациональных чисел, интервалов и комплексных чисел скрывают конкретное содержимое за конструкторами и селекторами. Урок по процедурным данным делает этот контракт еще более явным: пара или точка могут быть представлены замыканием, если остаются доступными те же наблюдаемые операции.

Процедуры для последовательностей и деревьев следуют форме своих данных. Разделяемая идентичность добавляет отдельный вопрос о размещении в памяти, тогда как цитирование отделяет данные в форме выражений от вычисляемого применения. Затем eq? и equal? отвечают на различные вопросы об идентичности и структуре.

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

Примеры с таблицей операций отделяют регистрацию от применения. Пакет устанавливает методы по ключам операции и типа, а apply-generic остается неизменным при добавлении нового представления. Башня арифметики аналогично удерживает правила повышения и понижения типов за пределами арифметики над значениями одного типа.

Примеры Хаффмана, комплексных чисел, приведения coercion и многочленов сохраняют собственные структурные инварианты. Урок по обобщенной арифметике добавляет более строгую границу упрощения: спроецированный результат понижается только после того, как его повторное повышение воспроизводит исходное значение.

Язык изображений применяет те же идеи абстракции геометрически. Рисователи описывают отрезки единичного квадрата, трансформации строят вложенные кадры и новые рисователи, и только финальное применение выводит строки SEG. Монохромный SVG создается на основе этой трассы Lispex, а не дублирующей модели рисования TypeScript.

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

    Исходные пятнадцать программ сохраняют задокументированные результаты своих уроков. Программа процедурных данных возвращает (left right 1 2 3). Цитирование возвращает ((+ x 3) + x (+ 10 3) #t #t #f). Представления множеств возвращают ((1 3 5) (4 1 3 5) #f (1 3 4 5 7)). Управляемая данными установка возвращает (4 8 rectangular swapped 3 4 25 3 4 25). Башня арифметики возвращает ((rational 7 2) (integer 1) (integer 5) (complex (rational 3 1) (rational 1 1))). Примеры изображений также выводят трассы SEG для визуализатора в рамках фиксированных ограничений среды выполнения.

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

    Найдите вызовы конструкторов и селекторов, создание замыканий и последующие сообщения, рекурсию по последовательностям и деревьям, идентичность размещения в памяти, границы quote, структурные сравнения, ранние выходы при работе с множествами, вызовы put/get таблицы операций, диспетчеризацию по тегам типов, пути raise и project, слияние членов многочленов, преобразование координат кадров, построение вложенных кадров и финальный вывод SEG. Трассы выполнения фиксируют именно выбранные запуски в рамках фиксированных ограничений среды выполнения.

    Повторение

    Двадцать вопросов для анализа систем данных

    От каких изменений защищает барьер абстракции?

    Ответ Пара служит полезным механизмом реализации, тогда как общедоступной идеей остается интерфейс. Сохранение знаний о представлении за стабильным интерфейсом делает последующие изменения локальными, а не распространяющимися по всей программе.

    Как рекурсивные процедуры следуют форме списка?

    Ответ Вектор использует соглашение об индексированной последовательности. vector-ref выбирает элемент по позиции, а vector-set! изменяет одну позицию без перестроения всего вектора.

    Как одна процедура работает на любой глубине дерева?

    Ответ Структура управления отражает определение данных. Благодаря этому соответствию процедура работает как с неглубоким списком, так и с глубоко вложенным деревом без отдельных ветвей для каждой глубины.

    Почему разделяемая идентичность отличается от простого равенства содержимого?

    Ответ Печатная метка #0= вводит разделяемый объект, а метка #0# ссылается на этот же объект повторно. Такие метки сохраняют идентичность, которую скрыла бы обычная нотация списков.

    Как конвейер последовательностей превращает вложенную рекурсию в именованные этапы?

    Ответ accumulate объединяет преобразованную последовательность с помощью + и начального значения 0. Полная программа по-прежнему использует рекурсию, но управление распределено по повторно используемым этапам, а не сосредоточено в одной специализированной процедуре.

    Как конструкторы данных отделяют алгебраическое упрощение от правил дифференцирования?

    Ответ make-sum и make-product отвечают за очистку представления. Они удаляют сложение с нулем, умножение на ноль или единицу и объединяют числовые операнды, поэтому ветви дифференцирования формулируют математические правила без повторения деталей упрощения.

    Как таблица операций сохраняет единый интерфейс для различных представлений?

    Ответ apply-generic удаляет метку только после того, как get выберет подходящую процедуру. Клиентский код указывает magnitude и передает данные с меткой, не проверяя, какое именно представление в них содержится. Добавление нового представления изменяет таблицу, а не обобщенный интерфейс.

    Как арифметика может использовать диапазон без зависимости от способа хранения двух его границ?

    Ответ Сложение объединяет две нижние границы и две верхние границы. Умножение должно учитывать все четыре произведения крайних точек, поскольку наименьший или наибольший результат может возникнуть на другой вершине, когда интервал пересекает ноль. Результатом является интервальная оболочка этих возможных произведений.

    Какая работа становится излишней, если представление множества гарантирует возрастающий порядок?

    Ответ union-ordered-set сравнивает две головы. Процедура сохраняет меньший элемент и продвигает этот список, а равные головы дают один элемент и продвигают оба списка. Каждый рекурсивный шаг потребляет хотя бы одну текущую голову, сохраняя упорядоченность без повторного поиска с самого начала.

    Как одно взвешенное дерево может определять как записываемые биты, так и восстанавливаемые символы?

    Ответ Кодирование проверяет, принадлежит ли следующий символ левой или правой ветви, и записывает 0 или 1 перед продолжением пути под этой ветвью. Декодирование обрабатывает те же решения в обратном направлении. Достижение листа выдает его символ и возвращает к корню для следующего кодового слова. Одинаковые веса могут допускать другое корректное дерево, но явное правило вставки фиксирует дерево, используемое в этих запусках.

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

    Ответ Первая программа создает 3 + 4i в обеих формах и опрашивает их через те же четыре селектора. Предикат close? фиксирует, что неточный тригонометрический путь совпадает в пределах объявленного допуска. Вторая программа складывает значения через вещественную и мнимую части и явно возвращает прямоугольный объект, а умножение объединяет модули и углы и возвращает полярный объект. Обобщенные селекторы проверяют абстрактные результаты, не раскрывая вызывающему коду внутреннее содержимое объектов.

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

    Ответ Вторая программа заменяет попарный выбор приведения упорядоченной башней типов. Процедура rank определяет уровни integer, rational и complex. Процедура raise выполняет только следующий шаг преобразования вверх, а raise-to повторяет его до тех пор, пока оба значения не достигнут старшего входного ранга. Сложение целого и рационального числа поэтому использует рациональное сложение. Сложение рационального и комплексного числа использует комплексное сложение после одного вызова raise. Несвязанная метка polynomial не имеет ранга и возвращает no-common-type. Башня устраняет неоднозначность для этих упорядоченных типов, но это не означает, что любой тип данных принадлежит единой иерархии или что проекция вниз всегда проходит без потерь.

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

    Ответ Вторая программа умножает один член на каждый член другого многочлена, сдвигает порядки сложением, перемножает коэффициенты и объединяет промежуточные произведения через add-terms. Умножение x + 1 на x − 1 создает два взаимно уничтожающихся средних члена, оставляя x² − 1. Отдельный вычислитель использует селекторы пакета и сообщает 8 при x = 3. Этот урок моделирует разреженные многочлены от одной переменной с целыми коэффициентами, включая сложение, умножение, нормализацию и вычисление с метками типов.

    Как один рисователь может описывать одно и то же изображение внутри квадратного, скошенного или масштабированного кадра?

    Ответ Первый запуск помещает рисователь рамки с диагоналями внутрь квадратного кадра. Второй запуск применяет рисователь ромба к скошенному кадру. Определения рисователей не содержат итоговых координат страницы. Каждый запуск выводит по одной строке SEG на каждый отображенный отрезок, а встроенное средство визуализации преобразует эти строки трассы в монохромный SVG. Возвращаемое значение (segments n) остается отдельным наблюдением.

    Как всего несколько преобразований кадра позволяют создать язык повторно используемых комбинаций изображений?

    Ответ Первый запуск размещает четыре преобразованных шеврона в квадрате. Второй запуск рекурсивно определяет right-split: левая половина сохраняет исходный рисователь, а правая половина объединяет по вертикали две уменьшенные копии. На глубине 3 базовый рисователь из трех отрезков порождает 45 отображенных отрезков. Средство визуализации отрисовывает в точности те строки SEG, которые выводит Lispex, и ограничивает только рендеринг в браузере, а не собственные пределы выполнения среды.

    Если конструкторы и селекторы удовлетворяют одному и тому же контракту, должно ли абстрактное значение данных иметь одно конкретное физическое представление?

    Ответ В примере с точкой используется диспетчеризация сообщений. make-point возвращает процедуру, отвечающую на сообщения x, y и sum. Селекторы знают только эти сообщения. Обычная пара, вектор или другое замыкание могут заменить эту реализацию, если сохраняется то же наблюдаемое поведение конструкторов и селекторов.

    Как вычислитель узнает, когда (+ x 3) является применением для запуска, а когда списком для анализа?

    Ответ eq? проверяет, обозначают ли операнды один и тот же символ или идентичный объект. equal? спускается по составным данным и сравнивает структуру и листья. Поэтому вторая программа отличает один разделяемый список от отдельного списка с тем же содержимым, признавая при этом их структурное равенство.

    Какие операции становятся менее затратными, когда представление множества гарантирует порядок или форму дерева поиска?

    Ответ Бинарное дерево поиска хранит меньшие элементы слева, а большие элементы справа. Каждое сравнение выбирает одну ветвь вместо сканирования каждого элемента. tree->list выполняет центрированный обход и восстанавливает упорядоченную последовательность. Сбалансированное построенное вручную дерево демонстрирует правило поиска, тогда как несбалансированное дерево показывает линейный путь.

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

    Ответ Вторая программа начинается только с метода описания для integer. Рациональное значение rational сначала приводит к no-method. Установка еще одной записи в таблицу позволяет тому же диспетчеру describe принять это значение без изменения диспетчера или пакета integer. Аддитивность здесь явная и конечная: расширение происходит путем регистрации еще одного метода под новым ключом.

    Как смешанная арифметика может находить общий тип и затем возвращаться к более простому типу без потери информации?

    Ответ drop выполняет обратное действие с осторожностью. Рациональное число проецируется в целое число только тогда, когда его знаменатель равен 1. Комплексное значение проецируется в свою действительную часть только тогда, когда его мнимая часть в точности равна нулю. Спроецированное значение снова повышается в типе и сравнивается с исходным перед продолжением упрощения, поэтому ненулевая мнимая часть не может исчезнуть.