Язык запросов
Настоящая глава посвящена созданию базы данных "Родственные отношения". База может изменяться во время исполнения программы, с помощью добавления и удаления фактов, в процессе диалога с пользователем. Рассматривается язык запросов к базе данных, близкий к естественному языку. Вычисляются ответы на запросы.
В данной главе создается консольный проект в объектном стиле.
Язык 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