Определение отношений в программе
1.4. Создание консольных приложений
Для того чтобы создать консольное приложение в системе Visual Prolog следует войти в среду разработки и выбрать команду меню Project > New. В открывшемся диалоговом окне Project Settings в поле Project Name следует вписать имя проекта (например, chapter1), в поле Project Kind следует указать Console application. После нажатия кнопки Finish или Next создается проект (рис. 1.3) (при нажатии кнопки Next появляется окно установки дополнительных параметров).
Построить проект можно с помощью команды меню Build > Build (или кнопки B панели инструментов), скомпилировать — команды Build > Compile (или кнопки C панели инструментов). Всякий раз, когда система будет спрашивать о добавлении директивы include <…>, можно просто отвечать Add All.
Факты базы данных поместим в отдельный модуль. Для этого выделим корень дерева проекта и выберем команду меню File > New In New Package. Появится окно Create Project Item. Cлева выберем элемент Text File, в поле Name напишем имя family, а в поле Parent Directory впишем слово Exe (рис. 1.4).
Нажмем кнопку Create. В результате в директории Exe проекта будет создан файл family.txt. Этот файл можно открыть из дерева проекта.
Откроем файл и поместим в него факты базы данных (см. листинг 1.3).
clauses parent("Иван", "Мария"). parent("Анна", "Мария"). parent("Мария", "Павел"). parent("Мария", "Петр"). parent("Мария", "Елизавета"). spouse("Иван", "Анна"). spouse("Павел", "Юлия"). male("Иван"). male("Павел"). male("Петр"). female("Мария"). female("Анна"). female("Елизавета"). female("Юлия").Пример 1.3. Файл family.txt. База данных
Код программы, приведенной ниже (листинг 1.4), нужно поместить в файл main.pro (в имплементацию класса main).
Язык Visual Prolog — типизированный, поэтому предикаты необходимо объявлять. В объявлении предиката указывается его имя, ставится знак двоеточия, а затем в круглых скобках через запятую перечисляются имена доменов (типов данных) аргументов:
class facts - relatives parent: (string Родитель, string Ребенок).
Словом relatives обозначено имя базы данных. В объявлениях предикатов можно использовать комментарии специального вида. Слова Родитель и Ребенок в этом объявлении обозначают комментарии. Компилятор их игнорирует. Такие комментарии пишутся в одно слово с прописной буквы.
Предикаты объявляются в разделах class facts (если определяются только в виде фактов) или class predicates, а определяются в разделе clauses. Цель программы формулируется в разделе goal, который находится в файле main.pro. Обычно в разделе goal только вызывается некоторый предикат, который используется для составления запросов. В данном примере и всюду далее таким предикатом является run.
Раздел open имплементации класса main следует изменить следующим образом:
open core, console
После добавления имени класса в раздел open предикаты этого класса можно использовать без указания имени этого класса, например, вместо console::write писать просто write. Предикат write используется для вывода на печать своих аргументов, которых может быть произвольное конечное число.
class facts - relatives parent: (string Родитель, string Ребенок). spouse: (string Муж, string Жена). male: (string). female: (string). class predicates father: (string Отец, string Ребенок) nondeterm anyflow. mother: (string Мать, string Ребенок) nondeterm (o,o). clauses father(X, Y):- parent(X, Y), male(X). mother(X, Y):- parent(X, Y), female(X). run():- init(), file::consult("family.txt", relatives), father(X, Y), write("отец - ", X, ", ребенок - ", Y), nl, fail; mother(X, Y), write("мать - ", X, ", ребенок - ", Y), nl, fail; if father("Иван", "Петр") then write("\nИван является отцом Петра") else write("\nИван не является отцом Петра") end if, _ = readLine().Пример 1.4. Консольный проект "Родственные отношения"
Вывод решений для запроса, например для цели , организуется с помощью предиката fail. Этот предикат имеет значение ложь. Он вынуждает программу вернуться для поиска других решений. Его можно заменить любым ложным условием, например, 0 = 1. Когда перебор заканчивается, выполняется переход к подцели, стоящей после знака дизъюнкции ";". Для задания частного вопроса используется конструкция if-then-else-end if.
Ключевое слово nondeterm в объявлении предиката означает, что область истинности этого предиката может содержать более одного элемента или не содержать ни одного (см. п. 3.4). Ключевое слово anyflow означает, что некоторые аргументы предиката могут быть как входными, так и выходными. Последовательность (o,o) означает, что оба аргумента предиката — выходные, они возвращают некоторые значения (см. п. 3.5).
Предикат init инициализирует консоль, предикат consult загружает факты базы данных из файла в оперативную память. Программа обращается с ними так же, как если бы они были приведены в разделе clauses, вместе с другими предложениями.
Для ввода и вывода в консоли используются буфер ввода и буфер вывода, соответственно. Предикат clearInput очищает буфер ввода, предикат clearOutput очищает буфер вывода. Предикат readLine считывает содержимое буфера ввода в строку (string) и при этом полностью очищает содержимое этого буфера. В данном случае программа просто ожидает ввода любого символа.
Как создавать консольные приложения, описано также в [15] (дополнительно см. пример Visual Prolog Examples > _tutorial > family 1).