- •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.5.Более обобщенные процедуры
Рассмотренная ранее процедура sortработает с произвольным массивом целых чисел. Она оказалась бы более полезной, если бы смогла работать с различными массивами данных —символьными, строковыми или массивами действительных чисел. Мы можем достичь подобного обобщения, расширяя абстракцию через параметризацию и используя типы данных в качестве параметров.
Польза от применения типов в качестве параметров очевидна. Это следует из того факта, что многие встроенные типы данных, например массивы, записи и процедуры, параметризованы через типы.
При использовании типов в качестве параметров некоторые значения параметров могут не иметь смысла. Например, массивы могут быть отсортированы только в том случае, если элементы, принадлежащие к типу, упорядочены, ограничения на параметры типа предполагают набор некоторых заданных операций над ними. Спецификация абстракции должна содержать эти ограничения в предложении requires.
Некоторые спецификации для параметризованных процедур показаны на рис. 3.7,Отметим, что эти спецификации отличаются от непараметризованных процедур только заголовком, который содержит дополнительную часть, содержащую параметры. Предложение requiresпомимо прочих ограничений содержит ограничения, накладываемые на параметры. Если ограничения отсутствуют, то предложение requiresможет быть опущено. Например, абстракция sortтребует, чтобы параметр tимел операцию It, упорядочивающую t.Аналогично процедура searchтребует, чтобы ее параметр поддерживал как операцию, так и операцию equal. (Ltесть имя, используемое для операций, для которых допускается сокращенная форма <, a equalесть имя операции с сокращенной формой ==.)
sort = pi-ос [t: type] (a: array [t])-returns (array [t])
requires tимеет операцию
It: proctype (t, t) returns (bool) которая упорядочивает t. modifies a
effectsВозвращает новый массив с теми же границами, что и а, и содержащий элементы а, расположенные в возрастающем порядке; порядок устанавливается через t$lt.
search == proc [t: type] (a: array [t], x: t) retunis (int)
requires tимеет операции
equal, It: proctype (t, t) returns (bool)
такой, что tупорядочивается через It,и массив а упорядочен по возрастанию через It.
effectsЕсли х принадлежит а, то возвращается iтакое, что a [i] = x; в противном случае возвращается high(а) + 1
Рис. 3.7.Спецификации параметризованных процедур.
Параметризованная абстракция в действительности представляет собой класс взаимосвязанных абстракций, определенных в одной спецификации. Например, параметризованная абстракция sortпредставляет собой не одну параметризованную процедуру, а целый класс процедур, одна из которых принимает в качестве параметра массив целых чисел, другая —строковый массив и т. д. Мы будем поименовывать такие процедуры сочетанием имени класса с именем типа, например sort[целочисленная] и sort[строковая]. Аналогично мы получим search[целочисленная] и search[действительная]. Подобные имена мы уже использовали во встроенных классах абстракций языка CLU —например, array[целочисленный].
Класс содержит элемент для каждого типа, который удовлетворяет ограничениям, заданным в предложении requires.Например, sort [int]находится в классе sort, a sort [bool] —нет (поскольку в языке CLUк булевым переменным неприменима операция It).Спецификация процедуры естественным образом получается из спецификации класса путем замены имени параметра его значением и удалением "из предложения requiresограничений на тип параметра(ов) (поскольку эти условия, задаваемые этими ограничениями, были удовлетворены). Следовательно, спецификация для search [int]совпадает со спецификацией для searchна рис. 3.3.
Исходя из соображений удобства при составлении, отладке и тестировании, желательно реализовать параметризованную абстракцию в виде одной программы. Язык CLUобеспечивает такую возможность. Например, параметризованная процедура используется для представления класса процедур. Такая процедура имеет заголовок, аналогичный заголовку в спецификации:
sort == proc [t: type ] (a: array [t ]) returns (array [t ])
Обычно операции, перечисленные в предложении requires. спецификации, используются и в реализации. Например, для реализации процедуры sortмы используем операцию It.Для проверки типа в таких операциях компилятор должен знать их имена, а также число и типы их аргументов и результатов. Мы даем такую информацию в предложении where,которое помещается сразу же за заголовком. Например,
sort = proc [t: type] (a: array [t] returns (array [int]) where t has It: proctype (t, t) returns (bool)
Единственные операции параметра типа, которые могут быть использованы в параметризованном модуле, перечислены в предложении where.
Предложение whereвключает в себя часть информации, приводимую в предложении requiresчасти спецификации. Предложение requiresвключает в себя как синтаксическую информацию (имена и типы требуемых операций), так и семантическую (смысл выполняемых операций). В соответствующем предложении where мы приводим только синтаксическую информацию, которой вполне достаточно для проверки типа.
Текст параметризованной процедуры аналогичен непараметризованной процедуре. Например, приведенная на рис. 3.8реализация процедуры searchотличается от реализации на рис. 3.4тем,
search == proc [t: type] (a: array [t], x: t) returns (int)
where t has It, equal: proctype (t, t) returns (bool) %реализация с использованием метода линейного поискаat = array [t ] i: int :== atjjjlow (a) while i (= at$high (a) do
if a [i ] = x then return (i) end %использование T$equal if a [i] (x %использование t$lt
then i := i + I else return (at$high (a) + 1) end end %конец цикла while return (i) end search
Рис. 3.8.Параметризованная абстракция search.
что элементы массива, принадлежащие ранее к целочисленному типу, имеют теперь тип t —параметр типа. Процедура search сравнивает элементы массива при помощи t$ltи t$equal.Эти обращения даются с использованием сокращенных форм.
Для использования параметризованной процедуры мы должны сначала указать значения для параметров, например search [int]. Следовательно, search [int](а) является разрешенным обращением, если а есть array [int],но search(а) таковым не является, поскольку для tне было указано никакого значения.
При задании параметра для типа компилятор проверяет, применимы ли к данному типу операции, указанные в предложенииwhere.Например, обращение search [array [int]]не допускается, поскольку array [int]не имеет операции it.Такая проверка гарантирует, что использованные в параметризованном модуле операции будут снабжены фактическими параметрами и что значения и типы аргументов и результатов соответствуют ожидаемым.
Параметризованная процедура должна содержать список операций в предложении where,даже если она использует эти операции неявным образом, обращаясь к другой параметризованной процедуре, использующей данные операции. Предположим, например, что мы создали параметризованные версии процедур для задачи сортировки слиянием, показанной на рис. 3.5,Операцию It использует только процедура merge.Однако процедуры merge и sortдолжны содержать Itв предложениях v^hereс тем, чтобы компилятор мог удостовериться, что сделанное в merge.sort обращение merge [t]является допустимым.