- •4.9.1. Изменяемость
- •4.9.2. Классы операций
- •4.9.3. Полнота
- •4.9.5. Операции egual, similar и copy
- •5. Исключительные ситуации
- •6.2.1. Сигнализация об исключительных ситуациях
- •5.2.2. Обработка исключительных ситуаций
- •5.2.3. Предложение resignal
- •6.2.4. Необрабатываемые исключительные ситуации
- •6.2.3. Предложение resignal
- •5.2.4. Необрабатываемые исключительные ситуации
- •6.2.1. Реализация итераторов
- •6.2.2. Использование итераторов
- •7.2. Абстракция данных
- •7.4. Генераторы
- •9.1.3. Пример
94 Глава 4
Абстракции данных
% %
Нет необходимости, чтобы представление упорядоченных списков было отсортировано. Однако эффективнее представление — это упорядоченное двоичное дерево. Каждый узел дерева содержит элемент и левое и правое поддеревья; каждое поддерево — это упорядоченный список. На языке CLU это выражается следующим образом.
node = record [val: t, right, left: olist [t]]
Как и для списков, пустой упорядоченный список рассматривается как специальный случай:
rep == variant [empty: null, some: node}
Это еще одно рекурсивное представление. Заметим, что оно использует изменяемые типы (variants и records), так как мы реализуем изменяемый тип. Теперь мы можем задать функцию абстракции
% Типичный упорядоченный список olist есть {е1, ..., еп] % Функция абстракции есть
А (г) = [ ] если r пуст = А (п. left) II [n.val] Ц А (п.right) если r не пуст,
где п = value, some (r)
Мы поддерживаем упорядочение представления, записывая все меньшие, чем значение узла, элементы в левое поддерево этого узла, а все большие — в правое поддерево. Таким образом, имеем
% Инвариант представления есть: % if is_some (r) then % все компоненты n.left < n.val % & n.val (всех компонентов n.right, % где п == value, some (r)
Если мы идентифицировали функцию абстракции и инвариант представления, реализации операций относительно определены (рис. 4.17). Реализации рекурсивны (итеративные реализации также возможны, но они более сложны).
olist = cluster [t: type] is create, addel, remel, is_in, least, empty where t has equal, It: proctype (t, t) returns (bool)
node = record [val: t, left, right; olist (t}] rep == variant [some; node, empty: null I
% Типичный упорядоченный список olist есть [el, ... en} % Функция абстракции есть
% A (r) = [] если r пуст % = A (n.left) [I [n. val ] [[ A (n.right) если r не пуст, % & где n = value_some (r) % Инвариант представления есть: % if is-some (r) then % все компоненты n.left (n.val % & n.val (всех компонентов n,right, % где n = value_some (r)
create = proc ( ) returns (cvt) return (rep$make_empty (nil)) end create
addel = proc (s: cvt, v: t) tagcase s tag some (n: node):
if v (n.val then addel (n.left, v) else addel (n.right, v) end tag empty:
rep$change_some (s, node$ {val: v, left: create (), right; create ()}) end end addel
remel = proc (s: cvt, v: t) tagcase s tag empty: return tag some (n: node):-if v == n.val then if empty (n.right) then % заменить узел на левое поддерево
rep$v .get. v (s, down (n.left))
else % сделать n.val значением из правого поддерева n.val := least (n.right) remel (n.right, n.val) end
elseit v (n.val then remel (n,left, else remel (n.right, v) end end end remel
least = proc (s: cvt) returns (t) tagcase s tag empty: tag some (n: node): if empty (n.left) then return (n.val) else return (least (n.left)) end end end least
lgJn = proc (s: cvt, v: t) returns (bool) tagcase s tag empty: return (false) tag some (n: node): if v = n.val then return (true)
elseif v (n.val then return (is_in (n.left, v)) else return (is-in (n.right, v)) end end end is_in
empty = proc (s: cvt) returns (bool) return (rep$is_empty (s)) end empty
end olist Рис. 4.17. Реализация упорядоченных списков.
9-5 Глава 4
В этой реализации скорость сортировки порядка loggn шагов для операций addel, remel, is-in и least при предположении, что дерево сбалансировано, т. е. что для каждого узла в правом и левом поддереве находится примерно одинаковое количество элементов. Если дерево несбалансировано, его можно сбалансировать, переставляя элементы случайным образом (используя благоприятный побочный эффект), однако здесь мы этого не делаем. Реализация не очень эффективна в смысле экономии пространства, так как требует хранить два указателя в каждом узле дерева.
Если бы наборы intset были реализованы с использованием упорядоченных списков, операция remove-dupis (см. ее реализацию на рис. 4.6) требовала бы порядка nioga п сравнений вслучае сбалансированного дерева. Один из случаев несбалансированного дерева — случай, в котором сортируется массив, являющийся аргументом операции remove-dupis!
4.9. Обсуждение
В этом разделе мы будем обсуждать аспекты абстракций данных, которые интересны как для того, кто определяет абстракции, так и для того, кто использует или реализует эти абстракции. Темы для обсуждения: изменяемость, полнота, анализ свойств абстрактных объектов и смысл операций equal, similar и copy.
4.9.1. Изменяемость
Абстракции данных либо изменяемы (с объектами, значения которых могут изменяться), либо неизменяемы. К определению этого аспекта типа следует относиться внимательно. В общем случае тип должен быть неизменяемым, если его объекты естественным образом имеют неизменяющиеся значения. Это, например, имеет место для таких математических объектов, как целые числа, полиномы и комплексные числа. Как правило, тип должен быть изменяемым, если моделируется что-то из реального мира, как, например, запоминающее устройство или автомобиль в имитационной системе, где значения объектов естественным образом изменяются с течением времени. Например, автомобиль может двигаться или стоять, в нем могут находиться или не находиться пассажиры. Однако мы можем все равно предпочесть использовать в этом случае неизменяемый тип, потому что он обеспечивает большую надежность. Вот почему списки неизменяемы.
Принимая решение об изменяемости, иногда необходимо учесть соотношение эффективности и надежности. Неизменяемые абстракции надежнее, чем изменяемые, так как не возникает никаких проблем при разделении объектов. Однако для неизменяемых
Абстракции данных
абстракций объекты могут создаваться и уничтожаться чаще, что означает, что чаще будет необходима сборка мусора. Например, представлять наборы intset как списки, вероятно, не слишком хорошо, если часто используются операции insert и delete.
Заметим, что в любом случае изменяемость или неизменяемость—это свойство типа, а не его реализации. Реализация должна просто поддерживать это свойство абстракции.
4.9.2. Классы операций
Операции абстракции данных распадаются на четыре класса. 1. Примитивные конструкторы. Эти операции создают объекты соответствующего им типа, не используя никаких объектов в качестве аргумента. Примерами таких операций являются операции create для наборов intset и списков.
2. Конструкторы. Эти операции используют в качестве аргументов объекты соответствующего им типа и создают другие объекты такого же типа. Например, операции add и mul — конструкторы для полиномов, а операции cons и rest — конструкторы для списков.
3. Модификаторы. Эти операции модифицируют объекты соответствующего им типа. Например, операции insert и delete — модификаторы для наборов intset. Очевидно, что только изменяемые типы могут иметь модификаторы.
4. Наблюдатели. Эти операции используют в качестве аргументов объекты соответствующего им типа и возвращают результат другого типа. Они используются для получения информации об объектах. Сюда относятся, например, операции size, member и chose для наборов intset и операции coeff, degree и equal для полиномов.
Обычно примитивные конструкторы создают не все, а только некоторые объекты. Например, операция poly$create создает только одночленные полиномы, а операция intset$create создает только пустой набор. Другие объекты создаются конструкторами или модификаторами. Например, операция poly$add может быть использована для получения полиномов, у которых количество членов больше одного, а операция intset$insert — для получения наборов с многими элементами.
Модификаторы играют ту же роль для изменяемых типов, что и конструкторы для неизменяемых. Изменяемый тип может иметь как конструкторы, так и модификаторы; например, наборы intset могут иметь операцию copy, которая создает новый набор, содержащий те же элементы, что и ее аргумент. Иногда наблюдатели комбинируются с конструкторами или модификаторами; например, операция remh модифицирует массив и возвращает? удаленный элемент.
4 Ласков Б,, Гатэг Дж.
98 Глава 4