Операции up и down
Полезно представлять себе операцию up как окружение объекта типа представления непроницаемой заслонкой. Тому, кто использует операцию up, сквозь эту заслонку не видно представления. Значение объекта можно исследовать только вызовом примитивных операций, соответствующих типу объекта. Например, пользователь не видит массива, который представляет набор intset, но может сказать, какие элементы имеются в наборе, и может добавлять или удалять элементы, обращаясь к соответствующим операциям.
Операция down производит обратное действие по отношению к up — она убирает заслонку. Если бы для пользователя имелась возможность обращаться к down, то ему было бы доступно конкретное представление абстрактного объекта и выгоды абстракции данных были бы утеряны. Поэтому язык CLU позволяет использовать операцию down только в кластере, который реализует объект.
Чтобы избежать возникновения фальшивых объектов, ограничивается также и использование операции up. Если бы эту операцию можно было использовать вне определяющего кластера, было бы возможно создавать объекты, которые выглядели как объекты типа, но которые при передаче их операциям кластера в качестве аргумента имели бы другое, чем ожидается, представление, что приводило бы к всевозможным очень неприятным ошибкам. Поэтому, чтобы использование абстракций данных было выгодно, ограничение сферы применения операций up и down необходимо.
Как уже говорилось, если операция down используется внутри кластера, то она убирает заслонку и представление объекта становится видимым. Однако это представление есть просто другой абстрактный объект, и он защищен заслонкой, сконструированной его собственной реализацией, которая может быть либо определенным пользователем кластером, либо частью реализации языка CLU. Реальное представление объекта снова недоступно, но информация об объекте может быть получена при помощи операций. Так, внутри кластера intset нам недоступна внутренняя структура массива, который реализует intset, но вы можете исследовать и изменять интересующую нас информацию, используя операции работы с массивом.
Для операций up и down (и следовательно, для cvt) не генерируется никакого объектного кода. Они лишь информируют компилятор о том, что способ рассмотрения объекта изменился. Компилятор использует эту информацию для контроля типов. Так как контроль типов производится во время компиляции, эта информация не нужна при выполнении, хотя она и может быть полезна для отладки.
Функция абстракции
Любая реализация абстракции данных должна определять, как представляются объекты, принадлежащие к этому типу. Выбирая представление, тот, кто занимается реализацией, учитывает связь между объектами представления и абстрактными объектами. Предполагается, что определенные объекты представления будут соответствовать, определенным абстрактным объектам.
Эта взаимосвязь может быть определена функцией, которая называется функцией абстракции. Эта функция отображает объекты представления в абстрактные объекты:
Здесь А обозначает набор абстрактных объектов. Для каждого объекта представления r, А(r) является абстрактным объектом аA, который представляет r.
А: rep А
Рис. 1. Пример функции абстракции.
Функция абстракции – это важнейшая информация о реализации. Она определяет конкретное представление, т.е. то, каким образом объекты представления реализуют абстрактные объекты. Эта функция должна быть обязательно представлена в комментариях реализации. Описывая функцию реализации, мы иногда сталкиваемся с проблемой, связанной с тем, что, если спецификация типа неформальная, область изменения функции абстракции не может быть точно определена. Сейчас мы будем преодолевать эту проблему с помощью неформального описания «типичного» абстрактного объекта.
Для определения функции абстракции прежде всего определяем типичный элемент абстрактного типа. Это дает нам возможность говорить об абстрактных объектах. Затем можем определить функцию абстракции в терминах этого типичного объекта. Например, для наборов intset вы можете дать следующее описание:
Типичный набор intset есть {х1, ... хn}
Здесь для описания наборов intset используем понятие математического набора, точно так же, как делали это в спецификации intset. Затем пишем
/*Функция абстракции есть
А(r) = {r[i] | low(r) i high(r)},
*/
где {x | p(x)} есть набор всех таких х, что р(х) имеет значение true.
В качестве другого примера рассмотрим полиномы. Мы решаем представлять полином массивом, в котором i-й элемент содержит i-й коэффициент, если этот коэффициент не относится к нулевым коэффициентам справа или слева от значащих членов полинома. Это представление описываем следующим образом:
/*Типичный полином есть: c0 + c1x + c2x2+ ...
Функция абстракции для коэффициента с1 есть
ci = г [i], если low(r) i high (r)
= 0 в противном случае
*/
Эта функция абстракции определена только для массивов с неотрицательной нижней границей. Функции абстракции часто имеют заданные области определения, как, например, в рассматриваемом случае.
Функция абстракции, в частности, удобна тем, что устраняет двусмысленности в интерпретации представления. Например, предположим, что с помощью массивов мы реализуем стеки. Мы можем выбирать, каким образом увеличивать массив, когда в стек добавляется новый элемент. Этот наш выбор будет отражен в функции абстракции. Если вы решаете увеличивать старший индекс массива и по этому индексу записывать элемент, то функция абстракции будет следующая:
/*Типичный стек – это последовательность [е1,...,еn], где
еn – элемент со старшим индексом
Функция абстракции есть
А(г) = [r[low (r)], ..., r [high (r)]]
Если вы решаете уменьшать младший индекс и по этому индексу записывать элемент, то
А(r) = [r[high(r)], ..., r[low(r)]]
*/
Заметим, что реализации стеков, о которых только что говорилось, используют одно и то же представление, которое интерпретируется, однако, по-разному.
Пример функции абстракции для стека
╔════════════════════════════════════════════════════════╗
║ Дескриптор стека REP (Представление стека) ║
║ ┌────────────┐ ========================= ║
S1─────╫─>│ Size │ ║
║ ├────────────┤ ║
║ │ TopInd │ ║
║ ├────────────┤ ║
║ │ PBody │ ║
║ └─────┼──────┘ ║
║ │ ║
║ ┌─────┼─────────────────────────────────────────────┐ ║
║ │ │ Тело стека │ ║
║ │ │ ┌───────────┐ │ ║
║ │ │ Size │-----------│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ ...│-Свободно--│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ │-----------│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ TopInd │///////////│ Вершина │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ ...│///////////│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ 3 │/Заполнено/│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ │ 2 │///////////│ │ ║
║ │ │ ├───────────┤ │ ║
║ │ └────────> 1 │///////////│ │ ║
║ │ └───────────┘ │ ║
║ │ ////////////////////// │ ║
║ │ //// Дно стека //// │ ║
║ └───────────────────────────────────────────────────┘ ║
╚════════════════════════════════════════════════════════╝
