Создание интерфейса к пролог-программе в среде C++ Builder

Май 21, 2010

Автор: dexis

7 Комментария(ев)

prolog
Prolog – достаточно гибкий и мощный язык логического программирования. Не смотря на то, что сегодня он утратил свою былую популярность, его все еще продолжают использовать для решения некоторых специфических задач, и, в частности, для разработки экспертных систем и баз знаний. Принцип использования этого языка в реальных проектах обычно следующий: на прологе создается база знаний, интерфейс к которой реализуется на «обычных» языках программирования.

На официальном сайте Amzi! Вы можете найти Amzi! Prolog + Logic Server – реализацию пролога, позволяющего создавать экспертные системы с использованием клиент-серверной технологии, когда база знаний хранится на сервере и с ней могут одновременно работать несколько клиентов. В комплекте также прилагаются библиотеки для работы с сервером на java, C\C++ и Delphi. Правда стоит все это не дешево.

Я же хотел рассказать о компоненте, который распространялся с более ранними версиями Amzi! Prolog и предназначался для организации интерфейса к пролог-программе в среде C++ Builder – LSEngine (Logic Server Engine). Скачать его можно здесь.

Данный компонент позволяет посредством большого числа свойств и методов производить запросы к скомпилированной пролог-программе (файл с расширением .xpl).

Установка компонента:

Для того, чтобы использовать компонент, его нужно сначала установить. Для этого его необходимо скачать, распаковать, затем открыть файл Package1.bpk и в открывшемся окне нажать кнопку Install.
После установки компонент помещается на вкладку Additional.

Создание проекта с использованием LSEngine:

Включение компонента в проект имеет некоторые нюансы, описанные ниже:

  1. Создадим новый проект: File->New->Application
  2. Сохраним его File->SaveAll
  3. В папку с проектом скопируем файлы A_MW3D.DLL, A_MW3D.LIB и AMZI.HPP они необходимы для корректной компиляции и работы программы
  4. Перейдем на вкладку Additional панели компонентов и поместим на форму компонент LSEngine
  5. В заголовочном файле модуля (по умолчанию Unit1.h) заменим #include “Amzi.h” на #include “Amzi.hpp”
  6. Подключим к проекту библиотеку A_MW3D.LIB, для этого выберем в меню Project->Add to project, выставим тип фалов .lib и укажем место расположения библиотеки
  7. После этого можно сохранить проект и он должен компилироваться без ошибок.

Создание простого приложения, реализующего интерфейс к пролог-программе:

В качестве примера возьмем следующую пролог-программу:

?View Code PROLOG
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:

prolog

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

Кратко опишу основные участки программы.

Как уже упоминалось, компонент работает с скомпилированными пролог-программами (файлами с расширением .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"

Константин написал:
24.05.2010

Отличный Пример. Огромное спасибо!!! если есть возможность, выложите материал по подобной технологии программирования))) борюсь с прологом водиночку, подсказать некому

dexis написал:
24.05.2010

Я знакомился с прологом в рамках курса рекурсивно-логического программирования. К сожалению, лекции имеются только в аналоговом виде

вот не плохой материал — я обращался туда почти по всем неясным вопросам:

http://www.mari.ru/mmlab/home/prolog/study_l.html

так же на сайте моего друга есть пример простой экспертной системы, построенной на описанной мною связке:

http://juravskiy.ru/?p=128

Константин написал:
06.06.2010

я имею ввиду как «скрестить» prolog и другие языки программирования. Delphi например.

dexis написал:
06.06.2010

Тот же самый компонент, я думаю, можно установить и на Delphi, они должны быть совместимы

Вовик написал:
07.01.2011

Отличная статья! Прога с коментами, в тексте все обьяснено! Автору огромное спасибо!

Виталий написал:
03.05.2011

Как жизнь молодая? :)

dexis написал:
03.05.2011

Привет, Виталик, замечательно )

Оставить комментарий

Имя : 
Почта : 
Сайт : 
Комментарий: