- •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.7.Списки
Наш следующий пример —'другой параметризованный тип, в котором требования налагаются на индивидуальные операции) а не на тип как целое. Кроме того, мы продемонстрируем рекурсивную реализацию. -
Неограниченные списки содержат элементы некоторого произвольного типа. Для них имеются операции создания пустого списка, формирования нового списка, состоящего из данного элемента и элементов уже существующего списка, и декомпозиции списка на первый элемент и остальные элементы. Спецификация представлена на рис. 4.12.Заметим, что списки —неизменяемые; такие операции, как cons,не модифицируют свои аргументы, а возвращают новый список.list = data type [t: type] is create, cons, first, rest, empty, equal Описание
Списки —это неизменяемые неограниченные последовательности элементов типа t.
Операции create = proc () returns (list [t])
effectsВозвращает пустой список.cons == proc (i: t, x: list [t]) returns (list [t]) effectsВозвращает список, у которого первый элемент i,а остальные
элементы —элементы x. first = proc (x: list [t]) returns (t) requires x —не пустeffectsВозвращает первый элемент x. rest == proc (x: list [t]) returns (list [t]) requires x —не пуст.
effectsВозвращает список, содержащий все элементы x,кроме первого.empty = proc (x: list [t]) returns (bool)
effectsВозвращает значение true,если x —пуст, и значение false —в про-тивном случае.
equal = proc (x,у; list [t]) requires tимеет операциюequal: proctype (t, t) returns (bool), т. e.условие равенства t. effectsВозвращает значение true,если xи у содержат одинаковые (в соотв.
с t$equal)и одинаково расположенные элементы.end list Рис. 4.12.Спецификация абстракции данных для списков.
Заметим, что списки как целое не налагают никаких требований на тип параметра. Таким образом, list [intset]—законный тип. Однако операция equalдолжна проверять элементы на равенство, поэтому она в предложении requiresналагает на tтребования. Следовательно, list [intset 1имеет все операции списков, кроме операции equal,так как intsetне имеет операции equal. Поскольку ограничивающие требования предъявлены только к операции equal,возможно создавать списки элементов любого типа (даже intset),но без операции equal.Определять списки таким образом можно потому, что они, как массивы, представляют собой очень общий группирующий механизм (массивы также имеют требования на частные операции вместо общих требований).
Удобный способ для реализации списков —это связанные пары, где первый элемент пары содержит элемент списка, а второй — ссылку либо на другую пару, либо на специальное значение, указывающее, что больше пар нет. Пример дан на рис. 4.13,а, где мы предполагаем, что t —целое число. Заметим, что если второй элемент пары указывает на другую пару, мы можем рассматривать эту вторую пару как начало списка, содержащего все элементы первоначального списка, кроме первого. Фактически, даже когда второй элемент не указывает на другую пару, мы тем не менее можем рассматривать его как указывающий на список — на пустой список. Такой подход иллюстрируется на рис. 4.13,б. В любом случае второй элемент пары указывает на другой список, и представление списков, таким образом, является рекурсивным.
На языке CLUпару можно представить следующим образом)pair = struct [elem: t, rest: list it]]
Тип данных пара (pair)подходит для представления списков, имеющих по крайней мере один элемент, но не подходит для представления пустых списков. Пустой список должен рассматриваться как особый случай: список либо имеет какие-то элементы и тогда представляется парой, либо список пуст. Для того чтобы
Рис, 4.13.Связанные пары: а—связанные пары, представляющие список(3 7 4);б—представление связанных пар, рассматриваемое рекурсивно, выразить концепцию списка, который либо имеет элементы, либо не имеет, нам необходим тип данных, который отражает это «либо-либо». Понятие oneofязыка CLUпредоставляет необходимое средство, и мы имеем
rep == oneof (some: pair, none: null] Пустой список представляется, как имеющий тип null.
list = cluster [t: type] is create, cons, first, rest, empty, equal rep = oneof [some: pair, none: null ] pair == struct [elem: t, rest: list [t]]
%Типичный список—это последовательность [sl,...,sn] %Функция абстракции есть
%А (г) === [ ]если тег г есть none % A (r) = [v.elem]Ц A (v. rest)если тег rесть some, %где v = value.some (r) %Инвариант представления есть% true create = proc ( ) returns (cvt) return (rep$make .none (nil)) end create
cons== proc (i: t, x: list [t]) returns (cvt) return (repgmake.some (pair$ {elem: i, rest: x))) end cons first = proc (x: cvt) returns (t) tagcase x
tag some (p: pair): return (p: elem) tag none: %Язык CLUтребует, чтобы
"/«если нет «других» ответвлений.end end first
rest = proc (x: cvt) returns (list [t]) tagcase x ' tag some (p: pair): return (p. rest) tag none: end end rest
empty = proc (x: cvt) returns (bool) return (rep$is_iione (x)) end empty
equal == proc (x, y: list [t]) returns (bool)
where t has equal: proctype (t, t) returns (bool) if empty (x) then return (empty (y)) elseif empty (y) then return (false) elseif first (x) = first (y) % t$equal
then return (rest (x) = rest (y)) % list [tl $equal else return (false) end end equal end list
все теги были указаны, даже
Рис. 4,14.Реализация списков.
Кластер списка показан на рис. 4.14.Как обычно, мы начинаем с определения представления: гео = oneof [some: pair, none: null ] pair == struct (elern: t, rest: list [t]]
Эти определения рекурсивны, так как представление определяется (через пару) в терминах списка. Рекурсивные определения типа в языке CLUограничиваются так, чтобы обеспечить рекурсивные представления абстракций данных: тип представления определяется (прямо или косвенно) в терминах этого типа.
Мы включили в кластер как комментарии описания функции абстракции и инварианта представления. Здесь определение функции абстракции дано в рекурсивном виде. (Вспомним, что операция value_someтипа oneof;она возвращает данные своего аргумента, если тег есть some.)
Операция createпросто создает представление списка с те-гом noneи возвращает его; эта операция автоматически (так как указано cvt)применяет к возвращаемому объекту операцию up и делает его абстрактным объектом. Операция consсоздает представление в тегом some;новая пара содержит в первом поле новый элемент, а во втором —ссылку на список, являющийся ее аргументом. Таким образом, заново созданный список разделяет заданный в качестве аргумента список с обратившимся к операции cons(рис. 4.15).Разделение допустимо, так как списки неизменяемые.
R Окружение
Вселенная объектов
О Опругкепае
Вселенная рёъектов
Рис. 4.15.Разделение в списках:а—представление списка (3)со ссылкой на переменную 1;б —результат вычисления m: list; ==list $ sons (4, 1).
Теперь обратим внимание на предложение where,которое следует за заголовком операции equal.В нем представлена синтаксическая часть информации предложения requiresспецификации. Требуемая операция используется в
first(х) - first(у)
Операция equalреализуется рекурсивно —она, используя короткую форму записи, обращается к самой себе.
rest(х) == rest(у)