Абриаль 2.1 Назад Начало Выше Дальше28/09/04 А.И.Пацкин [packin.ru]
Разработка веб-интерфейса ] Abrial DDL ] Конструирование БД ] [ Ассоциации ] Правила. Продукционное программирование ] Схема ядра ]

Ассоциации

Гипертабличное создание асоциаций ] Текстовое определение ассоциаций ]


Ассоциации в Абриале

Введение. Основные понятия.

Ассоциации.

Программирование в Абриале основано на ассоциациях и правилах. В этом разделе мы рассмотрим ассоциации.

Ассоциации это - виртуальные отношения, которые внешне выглядят как реальные отношения, фактически представленные в базе данных связями. Но ассоциации вычисляются динамически на основе отношений и ассоциаций. Кроме того есть примитивные ассоциации, которые аналогичны примитивным функциям (или встроенным операциям) в языках программирования.

Ассоциации, в отличие от функций, могут вычисляться во всех направлениях, а не только от аргументов к результату. Другое отличие: обычные функции выдают один результат за одно обращение, а ассоциация при едином обращении может генерировать целую таблицу результатов. В этом смысле ассоциация ведёт себя аналогично SQL-запросу.

Программирование в самом общем виде можно представить себе как процесс сборки или конструирования из простых или примитивных частей чего-то более сложного. Исторически в начале это была сборка программ из простейших операций и из вызовов других программ. Затем, с появлением функционального стиля, программисты стали собирать "сложные" функции из вызовов других более "простых" функций вплоть до "простейших" или встроенных функций. Появившееся затем объектно-ориентированное программирование, по сути, означало сборку из простых объектов более сложных или сборку более сложных и частных классов объектов из более простых и общих классов объектов.

В нашем случае программирование означает конструирование более сложных ассоциаций из более простых ассоциаций и/или отношений. При этом простейшими элементами являются либо отношения, реально представленные в базе данных, либо примитивные ассоциации, вычисление которых система берет на себя.

Программирование без языка программирования.

Программирование в Абриале означает создание через определенный нетекстовый интерфейс некоторых структур непосредственно в базе данных, минуя такую, считавшуюся прежде необходимой стадию, как программный текст. Проще говоря, программный текст более не нужен. И конечно, какой-либо язык программирования также перестал быть необходимым. Работа с программными структурами производится через табличный или другой наглядный интерфейс пользователя.

Однако для иллюстративных целей может быть полезной какое то подобие программного текста и формального языка. В текстовом виде простые примеры могут помещаться в документацию. Иногда результаты своей работы пользователю бывает удобней видеть тоже в виде текста. Для этого ниже в данном руководстве предлагается некоторая формальная нотация, во многом похожая на язык программирования.

Классическая и неклассическая вычислительная модель.

В Абриале применяется неклассическая вычислительная модель. Если сформулировать совсем кратко, то отличие от классической модели состоит в следующем. В "классической" функциональной модели основная задача состоит в вычислении выражений такого приблизительно вида

F(x,y,z)

Где F-функция, а x, y, z - аргументы этой функции, и аргументами могут быть константы, переменные или другие выражения. При этом предполагается, что аргументы в момент вычисления известны, а функция возвращает некоторый результат. В случае нашей модели задается соотношение между переменными

R(x,y,z)

где R-отношение/ассоциация, а x, y, z переменные, часть из которых известна (фиксирована), а часть - нет. При этом требуется определить множество наборов значений для неизвестных переменных, такое, что на всех этих наборах соотношение R(x,y,z) справедливо (истинно), или для частного случая, когда множество неизвестных пусто - только определить: истинно или нет данное соотношение на данном наборе фиксированных аргументов.

Простейший пример.

Пусть соотношение R это отношение Родитель(X,Y) задающее, что объект (Личность) Y является родителем объекта (Личности) X. Тогда если мы закрепим переменную X за объектом Андрей (X=Андрей), то получим Родитель(X=Андрей,Y) т.е. задачу получения всех родителей Андрея. Если мы зафиксируем переменную Y=Борис, т.е. Родитель(X,Y=Борис), то получим задачу получения всех детей Бориса, и, наконец, если мы зафиксируем обе переменные, т.е.

Родитель(X=Андрей,Y=Борис)

то получим задачу проверки истинности того, что Борис это родитель Андрея.

Однако это еще нельзя назвать программированием (или вычислением) т.к. отношение Родитель() реально имеется в базе данных. Поэтому перечисление всех родителей или детей объекта-личности есть всего лишь задача доступа к данным. Программирование и вычисление начинается в тот момент, когда мы начнем работать с отношениями, отсутствующими в базе данных и динамически вычисляемыми в процессе доступа.

Практический пример

Рассмотрим типичный практическую задачу. Есть база данных предприятия. В ней есть объекты классов: Сотрудник, Отдел, Подразделение. Все Сотрудники работают в некоторых Отделах и это отражается отношением Работает. Каждый Отдел входит в к некоторое Подразделение. Отделов, скажем, штук 30, а Подразделений всего три. Принадлежность Отдела Подразделению отражает отношение Входит . По смыслу каждый Сотрудник работает в одном определенном Подразделении, именно в том, в которое входит его Отдел. Но непосредственно эта связь между Сотрудником и Подразделением в данных никак не отражена. Между тем иметь прямой доступ от Подразделения ко всем его Сотрудникам и наоборот - от Сотрудника к его Подразделению -довольно типичная потребность. Чтобы ее удовлетворить приходится прибегать к программированию в той или иной форме.

При этом возникают следующие неприятные моменты. Во-первых, как правило это программирование производится не в самой концептуальной схеме базы данных (где ему законное природное место), а внутри некой интерфейсной надстройки, т.е. внутри некоторого "приложения". Иными словами концептуальная структура базы данных теряет цельность, разрывается на куски и размазывается по приложениям. Во-вторых, программирование связей проводится обычно по отдельности с двух сторон. (А для тройных связей - с трех сторон, и т.д.). И в-третьих, когда число таких связей становится достаточно большим, и возникает надобность что-либо поменять, то оказывается, что изменить ничего невозможно без риска разрушить работающую систему, т.к. нельзя найти никаких концов!

В Абриале эта проблема разрешается кардинально просто. В описание данных добавляется всего одна ассоциация:

Сотр-Подр:LINK(Сотр(X)&{Подразделение},Подр(Y):&Сотрудник})
    Сотр-Подр(X,Y)=>Работает(Y,Z),Входит(Z,X).

Заметим, что даже грамматики таких формул для их создания знать не надо. Все делается через стандартный табличный интерфейс с выбором всех объектов из списка. При определенном навыке ввод такой формулы займет в среднем полминуты. И всё! Ассоциация мгновенно начинает работать, причем в обе стороны. Из любого объекта Сотрудник можно увидеть его Подразделение и перейти на этот объект. А из любого Подразделения видны и доступны для перехода все его Сотрудники.

Таким образом, мы получаем систему построения Ассоциаций из уже имеющихся ранее ассоциаций и отношений. Добавив в основание этой системы представительный набор примитивных отношений, выполняющих простейшие логические и арифметические операции и другие функции, мы получим полноценную систему программирования.

Примитивные ассоциации/отношения

В текущей версии Абриаля набор примитивных отношений весьма ограничен:

ADD(X,Y)
Сложение X + Y - результат остаётся в X
SUB(X,Y)
Вычитание X - Y - результат остаётся в X
MUL(X,Y)
умножение X * Y - результат остаётся в X
DIV(X,Y)
деление X / Y - результат остаётся в X
CAT(X,Y)
конкатенация X и Y - результат остаётся в X
INT(X,Y)
Создание в переменной X значения - целого числа. Число для инициализации берётся из (обычно константы) Y.
NUM(X,Y)
Создание в переменной X значения - действительного числа. Число для инициализации берётся из (обычно константы) Y.
STR(X,Y)
Создание в переменной X строчного значения. Строка для инициализации берётся из (обычно константы) Y.
TOINT(X,Y)
эта функция записывает значение из второго параметра в 1-й, конвертируя его при этом в целый тип INT.
TONUM(X,Y)
эта функция записывает значение из второго параметра в 1-й, конвертируя его при этом в действительный тип NUM.
TOSTR(X,Y)
эта функция записывает значение из второго параметра в 1-й, конвертируя его при этом в строчный тип STR.
NE(X,Y)
Сравнение на неравенство для значений и на неидентичность для всех остальных объектов.
EQ(X,Y)
Сравнение на равенство для значений и на идентичность для всех остальных объектов.
GE(X,Y)
Проверка условия X >= Y
GT(X,Y)
Проверка условия X > Y
LE(X,Y)
Проверка условия X <= Y
LT(X,Y)
Проверка условия X < Y
LET(X,Y)
эта функция записывает в старое значение X новое: Y с учетом старого типа X. Для правил этот примитив инициирует процесс проверки сети на соответствие.
SET(X,Y)
эта функция записывает значение Y на место X, старое значение X пропадает и его тип безразличен

Абриаль и Пролог.

Здесь читатель, знакомый с Прологом, должен был заметить, что описана очень похожая, если не та же самая задача, что и там.

Действительно, решается задача аналогичная задаче Пролога. Однако есть различие в том, что из задач Пролога у нас решаются не все, а только часть, и вычислять, как это может Пролог,

Родитель(X,Y)

т.е. получать всех родителей всех детей, Абриаль не умеет в принципе, поскольку Абриаль рассчитан на работу с необъятными базами данных а не локальными, как Пролог. Для того подмножества задач, которое Абриаль решает, используются совершенно другие, принципиально более эффективные методы, чем в Прологе. Коротко говоря, Абриаль - имеет объектно-ориентированную организацию данных, а Пролог ориентирован на факты, т.е. по сути, это реляционная система. Принципиальная разница в том в Прологе просматривается вся база данных подряд, в поиске фактов, соответствующих некоторому образцу, а в Абриале просматриваются только корреспонденты некоторого объекта по одному определенному аспекту.

Абриаль и SQL.

Другим аналогом вычислительной модели Абриаля является SQL. Также как и для SQL-запроса вычисления Абриаля, вообще говоря, возвращают некоторую таблицу. Следует сделать только оговорку, что наши таблицы могут быть не совсем нормальными с точки зрения реляционной теории, т.е. они могут иметь одинаковые строки и иногда - нулевое число столбцов при ненулевом числе строк. Например, когда все переменные отношения фиксированы, результирующая таблица содержит нулевое(!) число столбцов, но при этом, возможно, ненулевое число строк(!). Но принципиальное отличие Абриаля от SQL в том, что последний работает в реляционных системах, где таблицы имеют самостоятельное значение, и никаких объектов нет в принципе. Таблицы в Абриале напротив - могут существовать только как аспектные значения некоторых объектов.

Реляционные системы по самой своей природе рассчитаны на локальную базу данных, где таблицы (пусть даже очень большие) могут быть доступны целиком. Абриаль рассчитан на необъятные глобальные сети, где о традиционных реляционных операциях с таблицами уже не может быть и речи.

Пример ассоциации.

Ниже для представления примеров используется некоторое подобие языка, однако надо понимать, что область применения этого языка ограничивается именно такими примерами в тексте документации и отчасти - внутренним представлением (форматом записи) в файлах базы данных. Собственно для программирования, как уже отмечалось ранее, этот язык фактически не нужен.

Итак, пусть у нас имеются следующие описания отношений:

Родитель:LINK(Родители:{&Личность},Дети:{&Личность})
Пол:LINK(Пол:&Личность,Пол_для:{&Пол})

Это два бинарных (двухместных) отношения: первое - между объектами класса Личность, второе - между объектами класса Личность и объектами класса Пол. Теперь, считая эти отношения "реальными", т.е. фактически заданными в базе данных, определим на их основе ассоциацию Брат между двумя объектами класса Личность, которое рассчитывается динамически, но со стороны любого объекта класса Личность выглядит совершенно так же, как "настоящее" отношение Родитель.

Брат:LINK(Брат(X):{&Личность},Брат_для(Y):{&Личность})
:-Родитель(X,Z),Родитель(Y,Z),NE(X,Y),Пол(Y,Мужской).

Поясним, что здесь написано. Это обычное определение отношения, но с некоторыми добавками. Определяется двухместное отношение Брат, между двумя объектами класса Личность. Отношение состоит из двух аспектов: Брат и Брат_для. Оба аспекта множественные, т.е. допускают множество связей, что задается фигурными скобками {&Личность} вокруг имени домена, и оба аспекта ссылочные, что задается знаком & перед именем класса домена Личность. Брат(А,Б) означает, что объект Б является братом для объекта А, но необязательно наоборот, т.к. А может быть женского пола.

Добавлениями по сравнению с определением реального отношения являются две вещи. Во-первых, после имен аспектов Брат и Брат_для в скобках заданы списки переменных (X) и (Y), соответствующих этим аспектам. (Конкретно мы имеем в обоих списках по одной переменной). Во-вторых, за обычным определением отношения после знаков "=>" следует список условий, разделенных запятыми. И как раз этот список условий и определяет новое отношение через отношения, уже определенные или примитивные. В нашем конкретном случае этот список задает обычный смысл понятия "Брат". Братом объекта Х считается объект мужского пола, имеющий общего с Х родителя и не равный Х.

…Родитель(X,Z),Родитель(Y,Z)…

Эти два условия задают, что у объектов X и Y должен быть общий родитель, соответствующий "рабочей" переменной Z.

…NE(X,Y)…

Это условие, используя примитивное отношение NE, т.е. "не равно", задает то естественное ограничение, что никто не должен быть братом самому себе.

…Пол(Y,Мужской)…

Это условие отличает брата от сестры.

Посмотрим, как работает эта ассоциация на реальных объектах. Пусть мы имеем следующие объекты классов Личность и Пол:

Андрей:Личность
Борис:Личность
Вера:Личность
Геннадий:Личность
Дмитрий:Личность
Мужской:Пол
Женский:Пол

И пусть имеются следующие связи (факты) для данных объектов

Пол(Андрей,Мужской)
Пол(Борис,Мужской)
Пол(Вера,Женский)
Пол(Геннадий,Мужской)
Пол(Дмитрий,Мужской)
Родитель(Борис,Андрей)
Родитель(Вера,Андрей)
Родитель(Геннадий,Андрей)
Родитель(Дмитрий,Андрей)

Возьмем объект Борис. Аспект Родители для него содержит единственную связь с объектом Андрей, аспект Дети у него пуст, по аспекту Пол он связан с объектом Мужской. Но он имеет еще два аспекта: Брат и Брат_для. Какими связями заполнены они?

Если представить объект Борис в виде "фрейма", он будет выглядеть так

Борис:Личность[
  Родители={Андрей}
  Дети={}
  Пол=Мужской
  Брат={Геннадий,Дмитрий}
  Брат_для={Вера,Геннадий,Дмитрий}
]

Мы видим, что (как и в жизни) у Личности Борис два брата (Геннадий, Дмитрий), но сам он является братом для трех личностей (Вера, Геннадий, Дмитрий). Еще нагляднее работа ассоциации Брат видна на примере объекта Вера:

Вера:Личность[
  Родители={Андрей}
  Дети={}
  Пол=Женский
  Брат={Борис,Геннадий,Дмитрий}
  Брат_для={}
]

"Изюминка" ассоциаций в Абриаля

Мы видим также, что виртуальные аспекты, т.е. аспекты ассоциаций, со стороны объектов ничем не отличаются от "настоящих" аспектов.

В этом и состоит "изюминка" системы программирования Абриаля. Достаточно только точно определить условия, составляющие новое ассоциацию, и система обеспечит, чтобы со стороны всех объектов, которых эта ассоциация касается, создалась иллюзия, будто у них появились новые связи. Хотя эти связи "ненастоящие" т.е. вычисляемые "на лету", это не мешает пользоваться ими как настоящими, реально существующими в базе данных связями. Конечно, их нельзя удалить или изменить, но по этим связям можно переходить при навигации и на их основе можно рассчитывать другие ассоциативные связи.

В текущей версии системы ассоциации могут использоваться не только для "чтения" данных, но и для простейших расчетов, например расчетов итоговых сумм.


Все права защищены. © 2000-2004 РосНИИ ИИ
http://artint.ru
Copyright. © 2000-2004 by RRIAI
Александр Иосифович Пацкин [mailto:aleksandr@tochka.ru].
28/09/04

К началу документа.