- •4.9.1. Изменяемость
- •4.9.2. Классы операций
- •98 Глава 4
- •4.9.3. Полнота
- •100 Глава 4
- •4.9.5. Операции egual, similar и copy
- •102 Глава 4
- •104 Глава 4
- •5. Исключительные ситуации
- •108 Глава 5
- •110 Глава 5
- •6.2.1. Сигнализация об исключительных ситуациях
- •5.2.2. Обработка исключительных ситуаций
- •112 Глава 5
- •5.2.3. Предложение resignal
- •6.2.4. Необрабатываемые исключительные ситуации
- •114 Глава 5
- •6.2.3. Предложение resignal
- •5.2.4. Необрабатываемые исключительные ситуации
- •116 Глава 5
- •118 Глава 5
- •118 Глава 5
- •120 Глава 5
- •122 Глава 5
- •124 Глава 5
- •128 Глава 6
- •130 Глава 6
- •6.2.1. Реализация итераторов
- •6.2.2. Использование итераторов
- •132 Глава 6
- •134 Глава 6
- •136 Глава 6
- •138 Глава 6
- •140 Глава 7
- •142 Глава 7
- •7.2. Абстракция данных
- •144 Глава 7
- •146 Глава 7
- •148 Глава 7
- •150 Глава 7
- •7.4. Генераторы
- •162 Глава 7
- •154 Глава 7
- •156 Глава 7
- •162 Глава 8
- •164 Глава 8
- •166 Глава 8
- •168 Глава 8
- •170 Глава 9
- •172 Глава 9
- •174 Глава 9
- •176 Глава 9
- •9.1.3. Пример
- •178 Глава 9
- •180 Глава 9
- •182 Глава 9
- •184 Глава 9
- •186 Глава 9
- •188 Глава 9
- •190 Глава 9
- •192 Глава. 9
- •194 Глава 9
98 Глава 4
4.9.3. Полнота
Тип данных является полным, если он обеспечивает достаточно операций для того, чтобы все требующиеся пользователю работы с объектами могли быть проделаны с приемлемой эффективностью. Строгое определение полноты дать невозможно, хотя существуют пределы относительно того, насколько мало операций может иметь тип, оставаясь при этом приемлемым. Например, для наборов intset мы предоставили только операции create, insert и delete, и программы не могут получить никакой информации относительно элементов набора (так как не имеется наблюдателей). С другой стороны, если мы добавим к этим трем операциям только одну операцию size, то сможем иметь информацию об элементах набора (например, можем проверять на принадлежность, удаляя целые числа и анализируя, изменился ли размер), но тип в этом случае получится неэффективным и неприемлемым.
В общем случае абстракция данных должна иметь операции по крайней мере трех из четырех рассмотренных нами в предыдущем разделе классов. Она должна иметь примитивные конструкторы, наблюдатели и либо конструкторы (если она неизменяемая), либо модификаторы (если изменяемая).
Полнота типа зависит от контекста использования, т. е. тип должен иметь достаточно богатый набор операций для его предполагаемого использования. Если тип предполагается использовать в ограниченном контексте (таком, как, например, одна программа), то должно быть обеспечено достаточно операций только для этого контекста. Если тип предназначен для общего использования, желательно иметь богатый набор операций. Вот почему встроенные типы языка CLU имеют много операций.
Чтобы решить, имеет ли абстракция данных достаточно операций, установите прежде всего все, что пользователи могут пожелать делать с ней. Затем продумайте, как эти вещи могут быть осуществлены с использованием данного набора операций. Если что-то окажется слишком неэффективным или слишком громоздким (или и то и другое вместе), исследуйте, может ли этому помочь какая-нибудь дополнительная операция. Иногда существенное улучшение в производительности можно получить, просто открыв доступ к представлению. Например, мы можем исключить операцию member для наборов intset, так как эта операция может быть реализована вне типа при помощи других операций. Однако проверка на принадлежность набору — операция общего использования, и будет работать быстрее, если . выполняется внутри реализации. Следовательно, тип данных intset должен иметь эту операцию.
Может быть также слишком много операций в типе. В этом случае абстракция будет менее понятной, а ее реализация и ра-
Абстракции данных
бота с ней —более сложными. Введение дополнительных операций должно быть соотнесено с затратами на реализацию этих операций. Если тип является полным, его набор операций может быть. расширен процедурами, функционирующими вне реализации типа.
4.9,4. Аназиз типов данных
Чтобы показать, что программа, которая использует тип данных, корректна, мы должны проанализировать свойства объектов типа. Этот анализ осуществляется на абстрактном уровне, т. е. мы используем спецификацию типа и игнорируем его реализацию. Такой анализ подробно обсуждается в гл'. II.
Например, иногда полезно установить свойство, являющееся общим для всех объектов типа. Такое свойство называется инвариантом абстракции — оно по существу является абстрактным аналогом инварианта представления. Такое свойство подобно теореме о целых числах и, как и теорема, доказывается с помощью индукции. Чтобы установить инвариант абстракции, мы должны показать, что он сохраняется для всех объектов, произведенных типом. Так как объекты производят только примитивные конструкторы, конструкторы и модификаторы, мы можем использовать следующий метод.
1. Показать, что свойство сохраняется для всех объектов, возвращаемых примитивными конструкторами.
2. Показать, что свойство сохраняется для объектов, возвращаемых конструкторами, предполагая, что свойство сохраняется для объектов, передаваемых конструкторам в качестве аргументов.
3. Показать, что при выходе из модификатора свойство сохраняется для модифицированных объектов, предполагая, что свойство сохраняется при вызове модификатора.
Например, интересное свойство упорядоченных списков (см. рис. 4.6) заключается в том, что они не содержат дублирующих друг друга элементов (что определяется операцией t$equal). Этот инвариант полезен для определения корректности реализации наборов intset с использованием упорядоченных списков. То, что это свойство является инвариантом, устанавливается следующим образом: 1) оно сохраняется для единственного примитивного конструктора create, так как операция create возвращает новый пустой список; 2) оцо сохраняется для операции addel (s, х). При возврате из этой операции в s содержатся те же элементы, которые там были до вызова, плюс элемент х. По предположению, до вызова в s нет дублирующих друг друга элементов. Кроме того, операция addel требует (в предложении requires), чтобы х не содержался в s. Следовательно, набор s с добавленным элементом х не содержит дублирующих друг друга элементов; 3) оно сохраняется для операции remel (s, х), так как оно сохраняется 4*
