Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
38
Добавлен:
23.03.2015
Размер:
884.22 Кб
Скачать

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

Соседние файлы в папке POSIBNIK