Создание интерфейса к пролог-программе в среде C++ Builder
Prolog – достаточно гибкий и мощный язык логического программирования. Не смотря на то, что сегодня он утратил свою былую популярность, его все еще продолжают использовать для решения некоторых специфических задач, и, в частности, для разработки экспертных систем и баз знаний. Принцип использования этого языка в реальных проектах обычно следующий: на прологе создается база знаний, интерфейс к которой реализуется на «обычных» языках программирования.
На официальном сайте Amzi! Вы можете найти Amzi! Prolog + Logic Server – реализацию пролога, позволяющего создавать экспертные системы с использованием клиент-серверной технологии, когда база знаний хранится на сервере и с ней могут одновременно работать несколько клиентов. В комплекте также прилагаются библиотеки для работы с сервером на java, C\C++ и Delphi. Правда стоит все это не дешево.
Я же хотел рассказать о компоненте, который распространялся с более ранними версиями Amzi! Prolog и предназначался для организации интерфейса к пролог-программе в среде C++ Builder – LSEngine (Logic Server Engine). Скачать его можно здесь.
Данный компонент позволяет посредством большого числа свойств и методов производить запросы к скомпилированной пролог-программе (файл с расширением .xpl).
Установка компонента:
Для того, чтобы использовать компонент, его нужно сначала установить. Для этого его необходимо скачать, распаковать, затем открыть файл Package1.bpk и в открывшемся окне нажать кнопку Install.
После установки компонент помещается на вкладку Additional.
Создание проекта с использованием LSEngine:
Включение компонента в проект имеет некоторые нюансы, описанные ниже:
- Создадим новый проект: File->New->Application
- Сохраним его File->SaveAll
- В папку с проектом скопируем файлы A_MW3D.DLL, A_MW3D.LIB и AMZI.HPP они необходимы для корректной компиляции и работы программы
- Перейдем на вкладку Additional панели компонентов и поместим на форму компонент LSEngine
- В заголовочном файле модуля (по умолчанию Unit1.h) заменим #include “Amzi.h” на #include “Amzi.hpp”
- Подключим к проекту библиотеку A_MW3D.LIB, для этого выберем в меню Project->Add to project, выставим тип фалов .lib и укажем место расположения библиотеки
- После этого можно сохранить проект и он должен компилироваться без ошибок.
Создание простого приложения, реализующего интерфейс к пролог-программе:
В качестве примера возьмем следующую пролог-программу:
man('pavel'). man('petr'). man('anton'). man('oleg'). man('vasya'). man('denis'). woman('vera'). woman('ira'). woman('anya'). woman('tanya'). woman('katya'). woman('yulya'). married('petr','vera'). married('anton','ira'). married('oleg','anya'). married('vasya','tanya'). husband(HUSBUND,WIFE):-married(HUSBUND,WIFE),man(HUSBUND),woman(WIFE). wife(WIFE,HUSBUND):-married(HUSBUND,WIFE),man(HUSBUND),woman(WIFE). suprug(X,Y):-husband(X,Y);wife(X,Y). |
В ней описывается база данных «Женатые пары» и правила, позволяющие находить мужа, жену и супруга (супругу).
Ниже приведен интерфейс для этой базы данных, разработанный с использованием компонента LSEngine в среде C++ Builder 6:
Программа позволяет добавлять правила в базу данных, удалять их и производить поиск по базе данных. Вы можете скачать программу с исходными кодами.
Кратко опишу основные участки программы.
Как уже упоминалось, компонент работает с скомпилированными пролог-программами (файлами с расширением .xpl). Это сделано для повышения производительности, но не всегда удобно, т.к. в такой файл нельзя вносить изменения (его нужно каждый раз компилировать заново). Гораздо удобнее хранить пролог-программу в обычном текстовом файле, и загружать все правила из него на этапе загрузки программы-интерфейса. Для этого можно использовать следующую функцию:
AnsiString BD,IS,S; IS="database.pro";//получаем имя файла BD=IS.SubString(1,IS.LastDelimiter("."))+"xpl";//собираем имя базы Memo1->Lines->Clear(); Memo1->Lines->LoadFromFile(IS);//открываем исходник if(FileExists(BD))//проверям существование базы данных { LSEngine1->InitLS(BD); LSEngine1->LoadXPL(BD);//если есть открываем }else{ BD="clear.xpl";//если нет берем пустую LSEngine1->InitLS(BD); LSEngine1->LoadXPL(BD);//открываем ее for(int i=0;iLines->Count;i++) { S=Memo1->Lines->Strings[i]; if(S!="") { LSEngine1->AssertzPStr(S);//заполняем пустую базу из исходника } } } |
Для посылки запроса к базе данных и обработке ответа используется следующая функция:
void TForm1::ExecQuery(AnsiString query) { TTerm t;//термовый тип для результата if(LSEngine1->CallPStr(t,query))//если запрос выполнен { do{//цикл выдачи всех результатов if (LSEngine1->GetArity(t) == 1) Memo2->Lines->Add(" " +LSEngine1->GetPStrArg(t, 1) ); if (LSEngine1->GetArity(t) == 2) Memo2->Lines->Add(" " + LSEngine1->GetPStrArg(t, 1) + " " + LSEngine1->GetPStrArg(t, 2)); }while(LSEngine1->Redo());//получение следующего результата }else{ ShowMessage("Правило не выполнено"); } }; |
Здесь обрабатываются только ответы с арностью (количеством параметров) равной 1 или 2.
Для добавления терма в базу данных используется следующая функция:
void TForm1::AddTrem(AnsiString term) { TTerm t; LSEngine1->PStrToTerm(t,term);//перевод текста в терм LSEngine1->Asserta(t);//добавление в начало Memo1->Lines->Add(term);//добавление в исходник } |
И, наконец, удаления терма из базы данных:
void TForm1::DelTrem(AnsiString term) { TTerm t; for(int i=0;iLines->Count;i++)//поиск в исходнике { if(term==Memo1->Lines->Strings[i]) { Memo1->Lines->Delete(i);//если найден удаляем } } LSEngine1->PStrToTerm(t,term);//перевод текста в терм LSEngine1->Retract(t);//удаление } |
Из этих коротких фрагментов можно получить общее представление о работе с пролог-программой в среде C++ Builder.
Для справки приведу полный список свойств и методов компонента LSEngine:
Процедуры для подключения Пролога к приложению:
void __fastcall Init(AnsiString xplname);– инициализация базы
void __fastcall InitLS(AnsiString xplname);– инициализация базы
void __fastcall InitLSXP(void * p);– инициализация базы
void __fastcall InitLSX(void);– инициализация базы
void __fastcall AddLSX(AnsiString lsxname);– добавление базы
void __fastcall AddPred(AnsiString pname, Word parity, TExtPred pfunc);-добавление предиката
void __fastcall InitPreds(TPredInitPtr PIptr);– инициализация предиката
void __fastcall Load(AnsiString xplname);– чтение базы из файла
void __fastcall LoadXPL(AnsiString xplname);– чтение базы из файла
bool __fastcall Main(void);– доказательство главной цели
void __fastcall Reset(void);– сброс базы
void __fastcall Close(void);– закрытие базы
void __fastcall CloseLS(void);– закрытие базы
Функции и параметры предикатов
void __fastcall GetParm(int n, TDType dt, void * p);-);- получение параметра
AnsiString __fastcall GetPStrParm(int n);—получение строкового параметра
int __fastcall GetIntParm(int n);—получение целого параметра
int __fastcall GetLongParm(int n);—получение параметра типа длинное целое
int __fastcall GetShortParm(int n);—получение параметра типа короткое целое
float __fastcall GetFloatParm(int n);—получение параметра вещественного типа
TPType __fastcall GetParmType(int n);—получение типа параметра
int __fastcall StrParmLen(int n);-получение длины параметра
bool __fastcall UnifyParm(int n, TDType dt, void * p);-унификация параметра
bool __fastcall UnifyPStrParm(int n, AnsiString s);-унификация строкового параметра
bool __fastcall UnifyAtomParm(int n, AnsiString s);-унификация параметра типа атом
bool __fastcall UnifyIntParm(int n, int i);-унификация параметра целого типа
bool __fastcall UnifyLongParm(int n, int i);-унификация параметра типа длинное целое
bool __fastcall UnifyShortParm(int n, int i);-унификация параметра типа короткое целое
bool __fastcall UnifyFloatParm(int n, float f);-унификация параметра вещественного типа
Вызов Пролога из C++Builder
bool __fastcall Exec(void * &tp);-выполнение доказательства
bool __fastcall ExecStr(void * &tp, char * s);-выполнение доказательства, записанного в строке
bool __fastcall ExecPStr(void * &tp, AnsiString s);-выполнение доказательства, записанного в строке
bool __fastcall Call(void * &tp);-вызов терма
bool __fastcall CallStr(void * &tp, char * s);-выполнение терма, записанного в строке
bool __fastcall CallPStr(void * &tp, AnsiString s);-выполнение терма, записанного в строке
bool __fastcall Redo(void);-восстановление
void __fastcall ClearCall(void);-очистка стека вызовов
Добавление и удаление фактов из динамической базы данных
void __fastcall Asserta(void * t);-выполнение предиката Asserta
void __fastcall Assertz(void * t);-выполнение предиката Assertz
void __fastcall Retract(void * t);-выполнение предиката Retract
void __fastcall AssertaStr(char * s);-выполнение предиката Asserta над строковой записью
void __fastcall AssertzStr(char * s);-выполнение предиката Assertz над строковой записью
void __fastcall RetractStr(char * s);-выполнение предиката Retract над строковой записью
void __fastcall AssertaPStr(AnsiString s);-выполнение предиката Asserta над строковой записью
void __fastcall AssertzPStr(AnsiString s);-выполнение предиката Assertz над строковой записью
void __fastcall RetractPStr(AnsiString s);-выполнение предиката Retract над строковой записью
Преобразование строк и термов
void __fastcall TermToStr(void * t, char * s, int n);-преобразование терма в строку
void __fastcall TermToStrQ(void * t, char * s, int n);-преобразование терма в кодовую строку
void __fastcall StrToTerm(void * &tp, char * s);-преобразование строки в терм
AnsiString __fastcall TermToPStr(void * t);-преобразование терма в строку
AnsiString __fastcall TermToPStrQ(void * t);-преобразование терма в кодовую строку
void __fastcall PStrToTerm(void * &tp, AnsiString s);-преобразование строки в терм
Создание типов Пролога
void __fastcall MakeAtom(void * &tp, AnsiString s);-создание атома
void __fastcall MakeStr(void * &tp, char * s);-создание строкового типа
void __fastcall MakePStr(void * &tp, AnsiString s);-создание строкового типа
void __fastcall MakeInt(void * &tp, int i);-создание целого типа
void __fastcall MakeFloat(void * &tp, float f);-создание вещественного типа
void __fastcall MakeAddr(void * &tp, void * p);-создание адреса
Получение значения термов Пролога
TPType __fastcall GetTermType(void * t);-получение типа терма
void __fastcall GetTerm(void * t, TDType dt, void * p);-получение терма
AnsiString __fastcall GetPStrTerm(void * t);-получение имени терма
int __fastcall GetIntTerm(void * t);-получение значения терма типа длинное целое
int __fastcall GetLongTerm(void * t);-получение значения терма типа длинное целое
int __fastcall GetShortTerm(void * t);-получение значения терма типа короткое целое
float __fastcall GetFloatTerm(void * t);-получение значения терма вещественного типа
Работа с функторами
void __fastcall GetFA(void * t, AnsiString &s, Word &ap);-получение арности функтора
AnsiString __fastcall GetFunctor(void * t);-получение функтора
int __fastcall GetArity(void * t);-получение арности
void __fastcall MakeFA(void * &tp, AnsiString s, Word a);-создание арности функтора
bool __fastcall UnifyArg(void * &tp, int n, TDType dt, void * p);-унификация арности функтора
bool __fastcall UnifyPStrArg(void * &tp, int n, AnsiString s);-унификация арности функтора
bool __fastcall UnifyAtomArg(void * &tp, int n, AnsiString s);-унификация аргументов
bool __fastcall UnifyIntArg(void * &tp, int n, int i);-унификация целого аргумента
bool __fastcall UnifyLongArg(void * &tp, int n, int i);-унификация длинного целого аргумента
bool __fastcall UnifyShortArg(void * &tp, int n, int i);-унификация короткого целого аргумента
bool __fastcall UnifyFloatArg(void * &tp, int n, float f);-унификация аргумента вещественного типа
void __fastcall GetArg(void * t, int n, TDType dt, void * p);-получение аргумента
AnsiString __fastcall GetPStrArg(void * t, int n);-получение строкового аргумента
int __fastcall GetIntArg(void * t, int n);-получение целочисленного аргумента
int __fastcall GetLongArg(void * t, int n);-получение аргумента типа длинное целое
int __fastcall GetShortArg(void * t, int n);-получение аргумента типа короткое целое
float __fastcall GetFloatArg(void * t, int n);-получение аргумента вещественного типа
TPType __fastcall GetArgType(void * t, int n);-получение типа аргумента
int __fastcall StrArgLen(void * t, int i);-получение длины строкового аргумента
bool __fastcall Unify(void * t1, void * t2);-выполнение унификации
Работа со списком
void __fastcall MakeList(void * &tp);-создание списка
void __fastcall PushList(void * &tp, void * t);-помещение элемента в список
int __fastcall PopList(void * &tp, TDType dt, void * p);-извлечение элемента из списка
int __fastcall PopPStrList(void * &tp, AnsiString &s);-извлечение строкового элемента из списка
int __fastcall PopIntList(void * &tp, int &i);-извлечение целочисленного элемента из списка
int __fastcall PopLongList(void * &tp, int &i);-извлечение элемента типа длинное целое из списка
int __fastcall PopShortList(void * &tp, int &i);-извлечение элемента типа короткое целое из списка
int __fastcall PopFloatList(void * &tp, float &f);-извлечение элемента вещественного типа
Работа с потоками
void __fastcall SetStream(TPStream st, int i);-открытие потока
int __fastcall GetStream(TPStream st);-получение потока
void __fastcall SetInput(TGetC pfunc1, TUngetC pfunc2);-установка вводного потока
void __fastcall SetOutput(TPutC pfunc1, TPutS pfunc2);-установка выводного потока
Получение версии языка
void __fastcall GetVersion(AnsiString &s);-получение версии языка
AnsiString __fastcall GetPVersion();-получение версии языка
Обработка ошибок
void __fastcall ErrMsg(char * s);-сообщение об ошибке
AnsiString __fastcall ErrPMsg();-сообщение об ошибке
void __fastcall ErrRaise(AnsiString s);-защищенный блок
void __fastcall ErrReadBuf(int &i, char * s);-чтение буфера ошибки
Комментарии к "Создание интерфейса к пролог-программе в среде C++ Builder"
Отличный Пример. Огромное спасибо!!! если есть возможность, выложите материал по подобной технологии программирования))) борюсь с прологом водиночку, подсказать некому
Я знакомился с прологом в рамках курса рекурсивно-логического программирования. К сожалению, лекции имеются только в аналоговом виде
вот не плохой материал — я обращался туда почти по всем неясным вопросам:
http://www.mari.ru/mmlab/home/prolog/study_l.html
так же на сайте моего друга есть пример простой экспертной системы, построенной на описанной мною связке:
http://juravskiy.ru/?p=128
я имею ввиду как «скрестить» prolog и другие языки программирования. Delphi например.
Тот же самый компонент, я думаю, можно установить и на Delphi, они должны быть совместимы
Отличная статья! Прога с коментами, в тексте все обьяснено! Автору огромное спасибо!
Как жизнь молодая?
Привет, Виталик, замечательно )
Оставить комментарий