С этого шага мы начнем рассматривать создание и использование структур данных.
Аналогично переход от "очевидной", но простой структуры данных (такой, как связный список) к более сложной (например, бинарному дереву) может дать такие результаты,которые с лихвой окупят ваши усилия по усовершенствованию программы."
Рей Дункан. "PC Magazine", 1,1992, с.102
Абстрактный тип данных (АТД) - тип, который задается не способом построения значений этого типа из более простых, а совокупностью операций над ними и аксиом, которым должны удовлетворять эти значения и операции.
Этот термин появился в 1974 г. в статье Б.Лисков и С.Зиллеса [5], посвященной принципам разрабатывавшегося ими языка программирования CLU. Термин быстро распространился среди системных программистов и теоретиков программирования и стал у них не только популярным, но настолько модным, что им стали называть понятия, весьма далекие от того, о чем шла речь у его авторов. Прилагательное "абстрактный" может ввести в заблуждение (особенно математика), так как абстрактные типы не более абстрактны, чем многие другие варианты понятия типа, к которым это прилагательное не "прилагают". На самом деле речь идет прежде всего о повышении уровня абстракции по отношению к языкам императивного программирования.
Исходная интуитивная идея, стоящая за понятием АТД, - это группировка в одно понятие нескольких операций (действий) и множества (одного или более) объектов, к которым они применяются, и защита (скрытие) в языке и соответствующей ему системе программирования внутреннего представления АТД и действий, не указанных явно в определении АТД.
В языках программирования АТД - это конструкция (кластер, модуль, класс), состоящая из двух частей:
В некоторых экспериментальных языках, например в Альфарде [6,с.149], языковая конструкция, соответствующая АТД, содержит, кроме двух упомянутых, еще две части:
АТД в языке программирования, обеспечивающем защиту (скрытие) представления, но не представляющем абстрактного описания, называют инкапсулированным типом данных (ИТД). Этот термин, конечно, ближе к сути дела, так как "инкапсулировать" буквально и означает "помещать в капсулу, защищать оболочкой". В реализованных языках программирования используется только ИТД или еще более слабая форма АТД - без защиты.
К формальной записи абстрактных типов данных можно применить алгебраический подход [7, с.11-12]. Он проявляется в том, что задается множество операций над данными. Данные определяются как структуры, порождаемые этими операциями. Использование выразительных средств алгебры связано с возможностью применения к данным математических методов, в частности с перспективами применения к данным теории ассоциативных и коммутативных групп.
В.Н.Агафонов [6]
В качестве примера рассмотрим тип данных "список". В качестве операций над списками возьмем операцию создания списков CONS, операции декомпозиции списков CAR и CDR, операцию NULL, дающую возможность определить, пуст список или нет, а также операцию NIL, задающую пустой список.
Эти операции записываются следующим образом:
DATA_TYPE LIST (X) IS OPERATIONS: NIL : --> LIST(X) CONS: X x LIST(X) --> LIST(X) CAR : LIST(X) --> X CDR : LIST(X) --> LIST(X) NULL: LIST(X) --> BOOLEAN END LIST
Символом X в приведенных выше выражениях обозначается тип данных для каждого непустого элемента списка. Тип данных "список" называют полиморфным типом данных, поскольку он содержит внутри себя переменные для типов, как, например, X.
Если использовать только эти определения, то остаются неясными связи между операциями CONS, CAR и CDR, а также вид выражения NULL. Поэтому помимо задания операций записываются выражения (называемые обычно аксиомами), которым должны удовлетворять эти операции. Имеются три аксиомы списков:
DATA_TYPE LIST(X) IS AXIOMS: VAR Y: X, L: LIST(X) CONS (CAR (L), CDR (L)) = L NULL (NIL) = TRUE NULL (CONS (X,L)) = FALSE END LIST
С помощью определенных таким образом операций и аксиом формально определяется абстрактный тип данных: "список". Создание конкретных списков осуществляется таким образом, чтобы удовлетворить этим правилам.
Тип данных является полным, если он обеспечивает достаточно операций для того, чтобы все требующиеся пользователю работы с объектами могли быть проделаны с приемлемой эффективностью. Строгое определение полноты дать невозможно, хотя существуют пределы относительно того, насколько мало операций может иметь тип, оставаясь при этом приемлемым.
Полнота типа зависит от контекста использования. Если тип предполагается использовать в ограниченном контексте (таком, как, например, одна программа), то должно быть обеспечено достаточно операций для этого контекста. Если тип предназначен для общего использования, желательно иметь богатый набор операций [8, с.98].
Операции абстракции данных распадаются на 4 класса [8, c.97].
В общем случае абстракция данных должна иметь операции по крайней мере трех из четырех рассмотренных ранее классов.
Она должна иметь примитивные конструкторы, наблюдатели и либо конструкторы (если она неизменяемая), либо модификаторы
(если изменяемая) [8, с.98].
На следующем шаге мы рассмотрим создание и использование массивов.