Язык запросов
Настоящая глава посвящена созданию базы данных "Родственные отношения". База может изменяться во время исполнения программы, с помощью добавления и удаления фактов, в процессе диалога с пользователем. Рассматривается язык запросов к базе данных, близкий к естественному языку. Вычисляются ответы на запросы.
В данной главе создается консольный проект в объектном стиле.
Язык Visual Prolog — объектно-ориентированный. Классы и интерфейсы создаются с помощью диалогового окна Create Project Item. Классы могут иметь интерфейс, а могут не иметь интерфейса. Объекты порождают те, и только те классы, которые имеют интерфейс.
Декларация класса помещается в файл с расширением cl, интерфейс — в файл с расширением i, имплементация — в файл с расширением pro.
Рассмотрим пример интерфейса, декларации класса с этим интерфейсом и его имплементации.
interface person % интерфейс
open core
properties
id : unsigned.
name : string.
age : positive.
end interface person
%---------------------------------------------------------------------------
class person : person % декларация класса
open core
constructors % объявление конструктора
new: (unsigned Id).
predicates
count: () -> positive.
end class student
%---------------------------------------------------------------------------
implement person % имплементация класса
open core
facts
id : unsigned.
name : string := "".
age : positive := 18.
class facts
counter : positive := 0.
clauses
new(Id):-
id := Id,
counter := counter + 1.
count() = counter.
end implement person
%---------------------------------------------------------------------------
goal % цель программы
mainExe::run(main::run),
P1 = person::new(1),
P1:name := "Вася",
P1:age := 18,
P2 = person::new(2),
P2:name := "Маша",
P2:age := 19,
stdio::writef("%. % - %\n%. % - %\nКоличество = %",
P1:id, P1:name, P1:age, P2:id, P2:name, P2:age,
person::count()).
Пример
12.1.
Класс person с интерфейсом person
В разделе properties объявляются свойства. Обычно они определяются в имплементации класса с помощью фактов-переменных (см. выше). Определение свойства может выглядеть и следующим образом:
facts
n: positive.
clauses
age() = n:-
stdio::writef("Считано значение свойства age = %\n", n).
age(V):-
n := V,
stdio::writef("Свойству age присвоено значение %\n", n).
В декларацию класса и в интерфейс помещаются предикаты (домены, свойства и т. д.), видимые извне. Их можно вызывать в цели или в имплементации другого класса. Все предикаты и свойства, объявленные в декларации класса и в интерфейсе, если класс имеет интерфейс, должны быть определены в имплементации этого класса.
Конструкторы классов возвращают указатели на объекты этих классов, хотя объявляются и определяются в виде обычных предикатов. Для того чтобы вызвать метод объекта, следует написать имя переменной, хранящей указатель на объект, затем поставить знак двоеточия. После этого, как обычно, пишется свойство или имя предиката и его аргументы.
Подробнее об объектно-ориентированном программировании в языке Visual Prolog см. [17].
12.1. База данных
Данный параграф посвящен созданию базы данных (как обычно, вначале следует создать консольный проект).
Для реализации доступа к сведениям, хранящимся в базе данных, создается класс dbrel. Объект базы данных создается по имени файла, в котором хранятся факты базы данных.
В базе данных хранятся сведения об именах и фамилиях людей, их поле, отце, матери и супругах. Каждый человек имеет уникальный идентификатор. При записи сведений о новом человеке ему автоматически присваивается идентификатор. Для хранения записей используется два отношения — person/6 и spouse/2.
Ниже приведен интерфейс общения с базой данных (файл dbrel.i).
constants
male = "м".
female = "ж".
predicates
load: ().
save: ().
person_nd: (unsigned, string, string, string, unsigned, unsigned)
nondeterm (o,o,o,o,o,o) (o,i,o,o,o,o) (o,i,i,o,o,o) (o,o,o,i,o,o)
(o,o,o,o,i,o) (i,o,o,o,o,o) (i,o,o,o,i,o) (o,o,o,o,o,i) (i,o,o,o,o,i).
spouse_nd: (unsigned, unsigned) nondeterm (o,o) (i,o) (o,i).
getSex: (unsigned Id) -> string determ.
getNewId: () -> unsigned.
setPerson: (unsigned, string, string, string, unsigned, unsigned).
setSpouse: (unsigned, unsigned).
delPerson: (unsigned).
delSpouse: (unsigned, unsigned).
Пример
12.2.
Интерфейс dbrel
Конструктор объявляется так, как показано ниже (файл dbrel.cl).
constructors
new: (string FileName).
Пример
12.3.
Декларация класса dbrel
Имплементация приведена в листинге 12.4 (файл dbrel.pro).
facts
filename : string.
maxId : unsigned := 0.
clauses
new(FileName):-
filename := FileName.
facts - rel
person: (unsigned, string, string, string, unsigned, unsigned).
spouse: (unsigned IdHusband, unsigned IdWife).
clauses
load():-
try file::consult(filename, rel)
catch Error do
writef("Error %. Unable to load the database from "
"the file %\n", Error, filename)
end try,
setMaxId().
save():-
file::save(filename, rel),
write("\nИзменения сохранены в базы данных.").
person_nd(Id, Name, Surname, Sex, IdF, IdM):-
person(Id, Name, Surname, Sex, IdF, IdM).
spouse_nd(IdH, IdW):-
spouse(IdH, IdW).
getSex(Id) = Sex:-
person(Id, _, _, Sex, _, _),
!.
getNewId() = maxId:-
maxId := maxId + 1.
setPerson(Id, Name, Surname, Sex, IdF, IdM):-
assert(person(Id, Name, Surname, Sex, IdF, IdM)),
writef("Запись '% - % - % - % - % - %' добавлена\n",
Id, Name, Surname, Sex, IdF, IdM).
setSpouse(IdH, IdW):-
assert(spouse(IdH, IdW)),
writef("Запись '% - %' добавлена\n", IdH, IdW).
delPerson(Id):-
retractAll(person(Id, _, _, _, _, _)),
writef("Записи о % удалены\n", Id).
delSpouse(IdH, IdW):-
retractAll(spouse(IdH, IdW)),
writef("Запись '% - %' удалена\n", IdH, IdW).
predicates
setMaxId: ().
clauses
setMaxId():-
L = [Id || person(Id, _, _, _, _, _)],
L <> [],
!,
maxId := list::maximum(L).
setMaxId().
Пример
12.4.
Имплементация класса dbrel