- •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.Упорядоченные списки
1.2.1.Абстракция через параметризацию
Абстракция через параметризацию позволяет нам, используя параметры, представить фактически неограниченный набор различных вычислений одной программой, которая есть абстракция всех этих наборов. Рассмотрим следующий текст программы:
х*х+у*у
В ней описаны вычисления, в процессе которых квадрат значения, хранящийся в некоторой переменной х, складывается о квадратом значения, хранящегося в переменной у. С другой стороны, выражение lambda ^х, у: int.(х *х +у *у)
описывает набор вычислений, в процессе которых возводится в квадрат значение, хранияое в некоторой целочисленной переменной (к полученному результату мы временно ссылаемся как к^) и этот результат добавляется к квадрату значения, хранимого в другой целочисленной переменной, временно называемой нами у. В этом выражении мы называем х и у формальными параметрами, а выражение х*х+у*у—телом выражения. Мы производим вычисления, связывая формальные параметры с аргументами и вычисляя затем все выражение. Например,х,у: int.(х *х +у*у) (w, z)эквивалентно w*w + z*z
В более привычном представлении мы можем заменить приведенное выше выражение lambdaнаsquares == proc(х, у: int) returns (int) return(х *х+ у*у) end
а замену формальных параметров —на фактические и вычисление выражения можем выполнять через обращение к процедуре»squares (w, z)
Программисты часто используют абстракцию через параметризацию, даже не замечая этого. Например, предположим, что нам необходима процедура, которая сортирует массив целых чисел а. В дальнейшем, вероятно, понадобится отсортировать некоторый другой массив, возможно, в другом месте этой же программы. Однако маловероятно, что каждый такой массив будет иметь имя а. Мы, следовательно, используем абстракцию через параметризацию, обобщая этим процедуру и делая ее более универсальной.
Абстракция через параметризацию является важным средством повышения универсальности программ. Программа sort,сортирующая произвольный массив чисел, полезнее той, которая может работать только с конкретным массивом целых чисел. Дальнейшее абстрагирование позволяет еще больше обобщить программу. Например, мы можем определить абстракцию sort,которая работает над массивами как целых, так и действительных чисел или даже вообще над различными массивоподобными структурами.
Абстракция через параметризацию —весьма полезное средство. Она не только позволяет относительно просто описывать большое (даже бесконечное) число вычислений, но и является легко и эффективно реализуемой на языках программирования. Однако этот способ не позволяет полностью реализовать тот уровень обобщения, который можно достичь при работе с процедурами.
1.2.2.Абстракция через спецификацию
Абстракция через спецификацию позволяет нам абстрагироваться от процесса вычислений, описанных в теле процедуры, до уровня знания лишь того, что данная процедура должна в итоге реализовать. Это достигается путем задания для каждой процедурыспецификации,описывающей эффект ее работы, после чего смысл обращения к данной процедуре становится ясным через анализ этой спецификации, а не самого тела процедуры.
Мы пользуемся абстракцией через спецификацию всякий раз, когда связываем с процедурой некий комментарий, достаточно информативный для того, чтобы иметь возможность работы с ней без анализа тела процедуры. Одним из удобных способов составления подобных комментариев является использование парутверждений.Утверждение requires(илиначальное условие)задает на входе процедуры истинность или ложность некоторого условия. Обычно на практике правильное выполнение процедуры задается некоторым набором таких условий. (Это часто просто означает, что некоторое логическое утверждение истинно.) Утверждениеeffects(иликонечное условие)задает некоторое условие, которое предполагается истинным по завершению данной процедуры в предположении, что для этой процедуры было удовлетворено начальное условие.
Рассмотрим, например, процедуру sqrtна рис. 1.3.Поскольку в процедуре имеется спецификация, мы можем игнорировать тело процедуры. Обращение вида callу := sqrt(х) будет иметь следующий смысл: «Если при вызове процедуры х больше 0,то после выполнения процедуры у есть приближение квадратного корня из х». Отметим, что начальные и конечные условия позволяют нам не упоминать о значении у для случая, когда х не больше 0.
При анализе спецификации для уяснения смысла обращения к процедуре мы придерживаемся двух чечких правил:
1.После выполнения процедуры можно считать, что конечное условие выполнено.
2.Можно ограничитьсятолькотеми свойствами, которые подразумевает конечное условие.
Эти два правила демонстрируют два преимущества, предоставляемых абстракцией через спецификацию. Первое из них состоит в том, что использующие данную процедуру программисты не обязаны знакомиться с ее телом. Следовательно, они освобождены от необходимости уяснения подробностей выполнения описанных в теле вычислений, устанавливая, что процедура действительно извлекает квадратный корень из аргумента. Это является большим преимуществом при работе со сложными процедурами и даже с простыми, но использующими незнакомые алгоритмы.sqrt = proc (coef : real) returns (real) % requires coef>0
% effectsвозвращает приближение квадратного корня, из coel ans: real == coef/2.0; i : int := I while i<7 do
ans := ans — ((ans * ans — coef)/(2.0 * ans)) i:=i+l
end return (ans) end sqrt Рис. 1.3.Процедура sqrt,
Второе правило уточняет, что мы на самом деле абстрагировались от тела процедуры, позволив себе не обращать внимания -на несущественную информацию. Именно это «забывание информации» и отличает абстракцию от декомпозиции. Анализируя тело процедуры sqrt,пользователи данной процедуры могут извлечь для себя большое количество информации, не следующее из конечного условия, например, что sqrt (4)возвратит результат +2. Однако в спецификации мы говорим, что подобная информация о возвращаемом результате игнорируется. Этим мы утверждаем, что процедура sqrtесть абстракция, представляющая собой набор всех вычислений, при котором вычисляется «приближение квадратного корня из х».