- •1. Введение
- •1.1. Декомпозиция и абстракция
- •1.2. Абстракция
- •1.2.1. Абстракция через параметризацию
- •1.2.2. Абстракция через спецификацию
- •1.2.3. Виды абстракций
- •3. Процедурная абстракция
- •3.2. Спецификации
- •3.3. Спецификации процедурных абстракций
- •3.4. Реализация процедур
- •3.5. Более обобщенные процедуры
- •3.6. Создание процедурных абстракций
- •3.7. Заключение
- •4. Абстракции данных
- •4.1. Спецификации для абстракций данных
- •4.2.1. Реализация на языке clu
- •4.2.2. Замечания по поводу операций up и down
- •4.3. Использование абстракций данных
- •4.4. Реализация полиномов
- •4.5. Пояснения для понимания реализаций
- •4.5.1. Функция абстракции
- •4.5.2. Инвариант представления
- •4.6.3. Сохранение инварианта представления
- •4.5.4. Изменяемые представления
- •4.6. Параметризованные абстракции данных
- •4.7. Списки
- •4.8. Упорядоченные списки
3.2. Спецификации
Очень важно дать абстракциям четкие определения. В противном случае не могут быть достигнуты преимущества, рассмотренные в разд. 3.1. Например, мы можем заменить одну реализацию абстракции на другую только в том случае, если все то, что поддерживала старая реализация, поддерживает и новая. Элементом, создающим данную зависимость, является абстракция. Следовательно, мы должны знать, что она собой представляет.
Мы будем определять абстракции посредством спецификаций, которые создаются на языке спецификаций, причем последний может быть как формальным, так и неформальным. Преимущество формальных спецификаций заключается в том, что они имеют точно определенное значение. Неформальные спецификации легче читать и понимать, однако точное их содержание установить затруднительно, поскольку язык неформальных спецификаций точным не является. ^Несмотря на это, неформальные спецификации могут быть весьма информативны и составлены таким образом, что читатели без труда поймут их смысловое содержание. В нашей книге мы используем как формальные, так и неформальные спецификации, однако основной акцент делается на неформальных спецификациях. Формальные спецификации будут описаны в гл. 10.
Спецификация отлична от любой определяемой ей реализации абстракции. Все реализации сходны между собой, поскольку они реализуют одну и ту же абстракцию. Отличие их заключается в том, что это делается разными способами. Спецификация определяет их схожесть.
3.3. Спецификации процедурных абстракций
Спецификация процедуры состоит из заголовка и описания функции, выполняемого процедурой. Заголовок содержит имя процедуры, номер, порядок и типы входных и выходных параметров. Кроме этого, выходные параметры могут, а входные должны быть поименованы. Например, заголовок для процедуры remove, dupis есть
remove_dupls == proc (a: array [int]) а заголовок для процедуры sqrt есть sqrt == proc (x: real) returns (rt: real)
Информация в заголовке чисто синтаксическая; она описывает «форму» процедуры. Это аналогично описанию «формы» математической функции:
Г: integer -> integer
Ни в одном из случаев смысл действий, выполняемых процедурой или функцией, не описывается. Эта информация приводится в семантической части спецификации, в которой смысл выполняемых процедурой действий описывается на естественном (разговорном) языке, возможно расширенном привычными математическими обозначениями. В этом описании используются имена входных и выходных параметров.
На рис. 3.2 приводится шаблон спецификации для процедуры. Семантическая часть спецификации состоит из трех предложений — requires (требует), modifies (модифицирует) и effects (эффекты). Эти предложения должны появляться в указанном ниже порядке, однако предложения requires и modifies обязательными не являются.
рпагпе = proc (...) returns (..,)
requires % этот оператор задает необходимые требования modifies % этот оператор идентифицирует все модифицируемые входные
% данные
effects % этот оператор описывает выполняемые функции
Рис. 3.2. Шаблон спецификации для процедурных абстракций.
Предложение requires задает ограничения, накладываемые на абстракцию. Предложение requires необходимо в том случае, если процедура является частичной, т. е. если ее поведение для некоторых входных значений недетерминировано. Если же процедура глобальна, т. е. ее поведение определено для всех входных значений, то предложение requires может быть опущено. В этом случае единственными требованиями, предъявляемыми при обращении к процедуре, являются требования, указанные в заголовке, т. е. число и типы аргументов. Оператор modifies задает список имен входных параметров, модифицируемых процедурой. Если входные параметры не указаны, то это предложение может быть опущено. Наконец, предложение effects описывает работу процедуры со значениями, не охваченными предложением requires. Оно определяет выходные значения и модификации, производимые над входными параметрами, перечисленными в списке modifies. Предложение effects составляется исходя из предположения, что требования предложения requires удовлетворены. Однако в том случае, когда требования в предложении requires не удовлетворены, о поведении процедуры ничего не сообщается.
concat = proc (а., Ь: string) returns (ab: string)
effects по возврату ab есть новая строка, содержащая символы из а (в том порядке, в котором они расположены в а), за которыми следуют символы из b (в том порядке, в котором они расположены в Ь), remove_dupls = proc (a: array [int])
modifies а
effects Удаляет из массива а все повторяющиеся элементы. Нижняя гра^ ница а остается без изменений, однако порядок следования оставшихся элементов может измениться. Если, например, перед вызовом а == [1: 3, 13, 3, 6], то по возвращению массив а имеет нижнюю границу, равную 1, и содержит три элемента 3, 13 и 6, расположенных в некоторой неопределенной последовательности. search == proc(a: array [int], х: int) retunis (i; int) requires массив а упорядочен по возрастанию.
effects Если элемент х принадлежит массиву а, то возвращается значение i такое, что а [i ] = х; в противном случае значение i на единицу больше, чем значение верхней границы массива а.
Рис. 3.3. Спецификация процедурной абстракции.
На рис. 3.3 приведено несколько спецификаций. Процедура concat не модифицирует входные переменные, а процедура ге-move_dupls изменяет входной массив, что отмечено в предложении modifies. Отметим, что в спецификации процедуры remove. dupis дается пример. Наличие примеров облегчает понимание спецификации, и их рекомендуется использовать во всех подходящих случаях.
Процедуры concat и remove_dupls являются общими процедурами, поскольку их спецификации не содержат предложений requires. Процедура search является частичной; она выполняет свои функции только в том случае, если входной массив отсортирован. Отметим, что предложение effects ничего не говорит о результате работы процедуры в том случае, когда входной массив не отсортирован. В этом случае составитель процедуры может выбрать любой подходящий для него вариант; например, процедура search может в этом случае возвращать индекс, выходящий за границы массива, или индекс, лежащий в пределах границ, но не содержащий х, или даже зацикливаться.
Спецификации составляются на языке спецификаций, а не на языке программирования. Следовательно, рассмотренные ранее спецификации написаны не на языке CLU. Кроме этого, спецификации обычно принципиально отличаются от программ, поскольку они фокусируют свое внимание на описании самой абстракции, а не ее реализации. Мы вернемся к описанию спецификаций.
