- •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.Упорядоченные списки
4.2.1.Реализация на языке clu
Хотя, конечно, возможно реализовать типы на языках, которые специально не приспособлены для этой цели, более удобно, если тип может быть реализован как отдельный программный модуль. Язык CLUпредоставляет такую программную единицу. Эта единица называетсякластер. (CLU,отметим, —это первые три буквы слова cluster.) Кластер включает в себя следующее.
1.Заголовок, в котором придаются имена реализуемому типу и операциям. Этот заголовок похож на заголовок в спецификации.
2.Определение представления, выбранного в данной реализации.
3.Реализации примитивных операций. Дополнительные программы (процедуры и итераторы) также могут быть включены в кластер, но обращаться извне можно только к тем, которым приданы имена в заголовке.
Общий вид кластера представлен на рис. 4.4. dnarne = cluster is %список имен операцийre? =и здесь дается описание представления
%здесь помещаются реализации операций%если необходимо, здесь могут быть представлены%вспомогательные программы
end dnarne Рис. 4.4.Общий вид кластера.
Для представления наборов целых чисел с помощью массивов, например, мы указываем
гер = array tint ]
Эта строка выполняет две функции) информирует компилятор CLUо том, что в этом кластере представлением является массив целых чисел и что в теле кластера обозначение герможет использоваться в качестве аббревиатуры для этого представленного массивом типа.
Внутри кластера должны быть доступны как абстрактный тип, так и тип представления. Более того, должна иметься возможность перехода от одного типа к другому. Например, операция insertполучает в качестве аргумента набор целых чисел, но для реализации insertнеобходимо использовать массив, который представляет этот набор.
Возможность перехода от абстрактного типа к типу представления и обратно в языке CLUобеспечивают ^две специальные операции: upи down.Операция upиспользует в качестве аргумента объект представления и в качестве результата выдает абстрактный объект, а операция downосуществляет обратное действие. Для того чтобы обеспечить преобразование абстрактного типа в тип представления и обратно, каждый кластер имеет свою собственную версию операций upи down.Эти операции автоматически определяются компилятором CLU.Например, в кластере процедуры intsetкомпилятор порождает операции up и downсо следующими заголовками:
up = proc (a: array tint 1) returns (intset) down == proc (s: intset) returns (array [int])
Операции upи downмогут использоваться только в кластерах и всегда осуществляют переход от абстрактного типа к типу представления и обратно только для кластера, в котором они появились. Поэтому операции up и downне могут отрицательно повлиять на контроль типов языка CLU.
Хотя операции upи downмогут появиться, где угодно, среди операций кластера, они в основном используются в одном из двух случаев. В первом случае, когда операция использует в качестве аргумента абстрактный объект, для преобразования этого объекта в тип представления используется операция down.Во втором случае, когда операция возвращает заново созданный абстрактный объект, сразу же перед возвратом для преобразования объекта представления в абстрактный тип используется операцияup.Для удобства работы язык CLUпредоставляет специально зарезервированное слово cvt.Слово cvtможет использоваться как тип аргумента или результата в заголовках операций кластеров. Использование его как типа аргумента указывает, что фактический аргумент —абстрактного типа, неформальный —типа представления и что поэтому к фактическому аргументу сразу же после вызова должна быть неявно применена операция down и результирующий объект представления должен быть присвоен формальному аргументу. Когда cvtиспользуется как тип результата, это означает, что результирующий объект —абстрактного типа, но возвращаемый объект—типа представления и что поэтому прямо перед возвратом к возвращаемому объекту неявно должна быть применена операция up.Слово cvtне добавляет никакой функциональности, которая не была бы обеспечена операциями upи down.Оно обеспечивает только удобство, которое возникает из возможности довольно просто описывать общие случаи.
Теперь мы можем обратиться к представленной на рис. 4.5 реализации процедуры intset.
intset == cluster is create, insert, delete, member, size, choose rep = array [int]
create = proc ( ) returns (cvt) return (rep$new ( )) end create
insert = proc (s: intset, x: int)
if — member (s, x) then rep$addh (down (s), x) end end insert
delete = proc (s: cvt, x: int) j: int :== getind (s, x) if j <= rep$high (s) then s [j] := rep$top (s) % topвозвращает s [high (s)]
rep$remh (s) end end delete
member = proc (s: cvt, x: int) returns (bool) return (getind (s, x) <= rep$high (s)) end member
size ^ proc (s: cvt) returns (int^ return (rep$size (s)) end size
72Глава 4
choose = proc (s: cvt) returns (int)
return (rep$bottom (s)) % bottomвозвращает s [low (s) ] end choose
getind = proc (s: rep, x: int) returns (int) i: int : = rep$low (s) while i (== rep$high (s) do if x = s \i} then return (i) end i:=i+ I end return (i) end getind
end intset Рис. 4.5.Реализация типа данных intset.
Прежде всего обратим внимание на реализацию операцииcreate.Заголовок здесь следующий:
create = proc ( ) returns (cvt)
Следовательно, возвращается абстрактный объект, т. е. набор целых чисел. По оператору returnдолжен быть возвращен массив, но к этому объекту перед возвращением применяется операция up. Обратимся теперь к реализации операции insert.В этом слу-лае cvtне используется. Заголовок здесь следующий:
insert = proc (s: intset,х: int)
Следовательно, заданный пользователем абстрактный объект intsetне подвергается операции down.
Операция insertне возвращает никакого результата; вместо этого она модифицирует свой аргумент intset.В данной реализации каждый элемент набора встречается в представляющем этот набор массиве только один раз; следовательно, операция insert не может просто добавить целое число х в массив. Она должна прежде проверить, не содержится ли уже х в массиве. Для этой цели используется операция member.Аргументом этой операции является набор целых чисел, поэтому в заголовке insertне используется cvt.Заметим, что в обращении к операции member префикс «intset$»отсутствует; этот префикс не требуется, если операция вызывается внутри своего собственного кластера.
Если оказывается, что х не является членом s,к sявно применяется операция downи при помощи операции addhх добавляется в массив. Если х уже принадлежит s,массив не модифицируется. Заголовок операции deleteследующий:
delete = proc (s: cvt,х: int)
В этом случае к переданному в качестве аргумента абстрактному объекту intsetнемедленно применяется операция down и внутри операции delete sбудет иметь тип array [int].
Операция deleteдолжна прежде всего выяснить, принадлежит ли х s.Для этого используется внутренняя процедура getind.Если х есть в массиве, процедура getindвозвращает индекс, соответствующий расположению х, в противном случае возвращается число, большее верхней границы массива. Так как процедура getindне указана в заголовке кластера, она является внутренней для этого кластера, т. е. может быть вызвана только из этого кластера.
Если х принадлежит s,операция deleteпросто записывает верхний элемент массива в позицию х и затем уничтожает верхний элемент, используя операцию remh.При этом операция remh возвращает удаляемый элемент как результат, однако в нашем случае этот ненужный результат просто уничтожается.
Операция chooseпозволяет возвратить произвольный элемент s.Следовательно, при реализации этой операции мы можем действовать, как нам удобно. Один удобный выбор —это первый элемент массива, другой, тоже удобный, —последний элемент. Конечно, это приводит к тому, что операция chooseвозвращает обратившемуся к ней различные объекты, но, так как в спецификации chooseговорится, что выбор произвольный, не следует рассчитывать на возврат каких-то определенных объектов. Отметим, что в согласии со спецификацией операция chooseне изменяет переданного ей в качестве аргумента набора intset.