Х. Абельсон, Дж. Дж. Сассман, Дж. Сассман - Структура и интерпретация компьютерных программ (1108516), страница 33
Текст из файла (страница 33)
Построение абстракций с помощью данных(define (rotate90 painter)(transform-painter painter(make-vect 1.0 0.0)(make-vect 1.0 1.0)(make-vect 0.0 0.0)))А эта сжимает изображение по направлению к центру рамки30 :(define (squash-inwards painter)(transform-painter painter(make-vect 0.0 0.0)(make-vect 0.65 0.35)(make-vect 0.35 0.65)))Преобразования рамок являются также основой для определения средств комбинирования двух или более рисовалок. Например, процедура beside берет две рисовалки,трансформирует их так, чтобы они работали соответственно в левой и правой половинахрамки-аргумента, и создает новую составную рисовалку.
Когда составной рисовалке передается рамка, она вызывает первую из преобразованных рисовалок над левой половинойрамки, а вторую над правой половиной:(define (beside painter1 painter2)(let ((split-point (make-vect 0.5 0.0)))(let ((paint-left(transform-painter painter1(make-vect 0.0split-point(make-vect 0.0(paint-right(transform-painter painter2split-point(make-vect 1.0(make-vect 0.5(lambda (frame)(paint-left frame)(paint-right frame)))))0.0)1.0)))0.0)1.0))))Обратите внимание, как абстракция данных, и особенно представление рисовалок в виде процедур, облегчает реализацию beside. Процедуре beside не требуется ничегознать о деталях рисовалок-компонент, кроме того, что каждая из них что-то изобразит вуказанной ей рамке.Упражнение 2.50.Определите преобразование flip-horiz, которое обращает изображение вокруг горизонтальнойоси, а также преобразования, которые вращают рисовалки против часовой стрелки на 180 и 270градусов.30 Ромбовидные изображения на рисунках 2.10 и 2.11 были получены с помощью squash-inwards, примененной к wave и rogers.2.2.
Иерархические данные и свойство замыкания145Упражнение 2.51.Определите для рисовалок операцию below. Below принимает в качестве аргументов две рисовалки. Когда получившейся рисовалке передается рамка, она рисует в нижней ее половине припомощи первой рисовалки, а в верхней при помощи второй.
Определите below двумя способами —один раз аналогично процедуре beside, как она приведена выше, а второй раз через beside иоперации вращения (см. упражнение 2.50).Уровни языка помогают устойчивому проектированиюЯзык построения изображений использует некоторые из важнейших введенных намиидей, относящихся к абстракции процедур и данных. Базовая абстракция данных, рисовалки, реализуется при помощи процедурного представления, и благодаря этому нашязык может работать с различными графическими системами единым образом. Средствакомбинирования обладают свойством замыкания, и это позволяет нам легко возводитьсложные построения. Наконец, все средства абстракции процедур доступны нам длятого, чтобы абстрагировать средства комбинирования рисовалок.Нам удалось бросить взгляд и еще на одну существеннейшую идею касательно проектирования языков и программ. Это подход уровневого проектирования (stratifieddesign), представление, что сложной системе нужно придавать структуру при помощипоследовательности уровней, которая описывается последовательностью языков.
Каждыйиз уровней строится путем комбинации частей, которые на этом уровне рассматриваютсякак элементарные, и части, которые строятся на каждом уровне, работают как элементарные на следующем уровне. Язык, который используется на каждом уровне такогопроекта, включает примитивы, средства комбинирования и абстракции, соответствующие этому уровню подробности.Уровневое проектирование пронизывает всю технику построения сложных систем.Например, при проектировании компьютеров резисторы и транзисторы сочетаются (иописываются при помощи языка аналоговых схем), и из них строятся и-, или- элементыи им подобные, служащие основой языка цифровых схем31 .
Из этих элементов строятся процессоры, шины и системы памяти, которые в свою очередь служат элементами впостроении компьютеров при помощи языков, подходящих для описания компьютернойархитектуры. Компьютеры, сочетаясь, дают распределенные системы, которые описываются при помощи языков описания сетевых взаимодействий, и так далее.Как миниатюрный пример уровневого подхода, наш язык описания изображений использует элементарные объекты (элементарные рисовалки), создаваемые при помощиязыка, в котором описываются точки и линии и создаются списки отрезков для рисовалки segments->painter либо градации серого цвета в рисовалке вроде rogers.Большей частью наше описание языка картинок было сосредоточено на комбинировании этих примитивов с помощью геометрических комбинаторов вроде beside и below.Работали мы и на более высоком уровне, где beside и below рассматривались какпримитивы, манипулируемые языком, операции которого, такие как square-of-four,фиксируют стандартные схемы сочетания геометрических комбинаторов.Уровневое проектирование помогает придать программам устойчивость (robustness),то есть повышает вероятность, что небольшое изменение в спецификации потребует относительно малых изменений в программе.
Например, предположим, что нам нужно из31 Одиниз таких языков описывается в разделе 3.3.4.146Глава 2. Построение абстракций с помощью данныхменить картинку, основанную на рисовалке wave, которая показана на рисунке 2.9. Мыможем работать на самом низком уровне, изменяя конкретный вид элемента wave; можем работать на промежуточном уровне и менять то, как corner-split воспроизводитwave; можем на самом высоком уровне изменять то, как square-limit расставляетчетыре копии по углам. В общем, каждый уровень такого проекта дает свой словарь дляописания характеристик системы и свой тип возможных изменений.Упражнение 2.52.Измените предел квадрата рисовалки wave, показанный на рисунке 2.9, работая на каждом извышеописанных уровней.
А именно:а. Добавьте новые отрезки к элементарной рисовалке wave из упражнения 2.49 (например,изобразив улыбку).б. Измените шаблон, который порождает corner-split (например, используя только однукопию образов up-split и right-split вместо двух).в. Измените версию square-limit, использующую square-of-four, так, чтобы углы компоновались как-нибудь по-другому.
(Например, можно сделать так, чтобы большой мистер Роджерсвыглядывал из каждого угла квадрата.)2.3. Символьные данныеВсе составные объекты данных, которые мы до сих пор использовали, состояли, вконечном счете, из чисел. В этом разделе мы расширяем возможности представлениянашего языка, разрешая использовать в качестве данных произвольные символы.2.3.1. КавычкиРаз теперь нам можно формировать составные данные, используя символы, мы можемпользоваться списками вроде(a b c d)(23 45 17)((Norah 12) (Molly 9) (Anna 7) (Lauren 6) (Charlotte 3))Списки, содержащие символы, могут выглядеть в точности как выражения нашего языка:(* (+ 23 45) (+ x 9))(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))Чтобы работать с символами, нам в языке нужен новый элемент: способность закавычить (quote) объект данных.
Допустим, нам хочется построить список (a b). Этогонельзя добиться через (list a b), поскольку это выражение строит список из значений символов a и b, а не из них самих. Этот вопрос хорошо изучен по отношению кестественным языкам, где слова и предложения могут рассматриваться либо как семантические единицы, либо как строки символов (синтаксические единицы). В естественных языках обычно используют кавычки, чтобы обозначить, что слово или предложение2.3.
Символьные данные147нужно рассматривать буквально как строку символов. Например, первая буква «Джона» — разумеется, «Д». Если мы говорим кому-то «скажите, как Вас зовут», мы ожидаемуслышать имя этого человека. Если же мы говорим кому-то «скажите “как Вас зовут”»,то мы ожидаем услышать слова «как Вас зовут». Заметьте, как, для того, чтобы описать,что должен сказать кто-то другой, нам пришлось использовать кавычки32 .Чтобы обозначать списки и символы, с которыми нужно обращаться как с объектамиданных, а не как с выражениями, которые нужно вычислить, мы можем следовать томуже обычаю. Однако наш формат кавычек отличается от принятого в естественных языкахтем, что мы ставим знак кавычки (по традиции, это символ одинарной кавычки ’)только в начале того объекта, который надо закавычить.