Теория
Обычно программа на Прологе состоит из четырех основных программных разделов. К ним относятся:
-
раздел clauses (предложений);
-
раздел predicates (предикатов);
-
раздел domains (доменов);
-
раздел goal (целей).
Раздел clauses — это сердце Пролог-программы; именно в этот раздел записываются факты и правила, которыми будет оперировать Пролог, пытаясь разрешить цель программы.
Раздел predicates — это раздел, в котором объявляются предикаты и домены (типы) их аргументов.
Раздел domains служит для объявления всех используемых нами доменов, не являющихся стандартными доменами Пролога.
Раздел goal — это раздел, в который вы помещаете цель Пролог-программы.
Базы данных
Предикаты БД в Turbo Prolog описываются в разделе database, который должен располагаться перед разделом predicates. Все утверждения с предикатами, описанными в database, составляют динамическую БД.
БД называется динамической, так как во время работы программы у нее (БД) можно удалять любые содержащиеся в ней утверждения, а также добавлять новые. Динамическая БД (ДБД) может быть записана на диск и считана с него в оперативную память. В ДБД содержатся только факты, но не правила. В статической базе данных утверждения представлены фактами и являются частью кода программы.
Встроенные предикаты для работы с БД
Для добавления в базу данных используются следующие предикаты:
-
asserta(fact) // добавление в начало базы данных dbasedom
-
asserta(fact, dbaseName) // добавление в начало базы данных dbaseName
-
assertz(fact) // добавление в конец базы данных dbasedom
-
assertz(fact, dbaseName) // добавление в конец базы данных dbaseName
Загрузка фактов из файла:
-
consult(fileName) // загрузка в базу данных dbasedom
-
consult(fileName, dbaseName) // загрузка в базу данных dbaseName
Удаление факта:
-
retract(fact) // удаление из базы данных dbasedom
-
retract(fact, dbaseName) // удаление из базы данных dbaseName
Сохранение базы данных в файле:
-
save(fileName) // сохранение базы данных dbasedom в файле fileName
-
save(fileName, dbaseName) // сохранение базы данных dbaseName в файле fileName
Описание программы
База данных будет содержать одну таблицу — таблицу футболок, столбцы которой перечислены далее:
-
Name — название.
-
Price (RUB) — цена в рублях.
-
Sex — мужская или женская футболка.
-
Size (International) — международный размер футболки.
-
Size (Europe) — европейский размер футболки.
-
Color — цвет.
-
Material — материал футболки.
-
Production year — год производства.
Предполагаются следующие возможности программы:
-
Считывание базы данных из CSV-файла
-
Добавление новой записи в базу данных;
-
Удаление футболки по названию;
-
Изменение записи в базе данных;
-
Поиск футболки по названию;
-
Отображение всех футболок в консоли;
-
Сохранение базы данных в CSV-файл;
-
Удаление всех записей из базы данных;
-
Выход из программы;
Код
В самом начале идёт описание всех используемых в программе доменов, баз данных и предикатов:
Domains name, world, color, sex, material = string % домены строкового типа europe, year, price = integer % домены целочисленного типа file = datafile % домен типа file arr = string* % массив из строк integers = integer* % массив из целых чисел Database dt_shirt(name, price, sex, world, europe, color, material, year) % описание предиката БД Predicates repeat % зацикливает кусок кода do_mbase % создаёт главное окно программы и вызывает предикат menu menu % создаёт меню программы process(integer) % ждет ввода номера функции из меню и затем вызывает её clear_database % очищение базы данных error % сообщает об ошибке read_until_not_integer(integer) % ждет ввода целого числа write_all % вывод всех футболок в командную строку write_all(arr,integers,arr,arr,integers,arr,arr,integers) % вывод всех футболок в командную строку write_all_csv % запись всех футболок в csv файл write_all_csv(arr,integers,arr,arr,integers,arr,arr,integers) % запись всех футболок в csv файл read_rows() % считывает строки из csv файла front_string(string, string, string) % считывание одного значения из csv до разделителя ; read_prov(integer) % ввод номера функции из меню find(integer) % вызов предиката поиска в зависимости от введенного числа find_shirt_name(string) % поиск футболки по имени find_material(string) % поиск футболки по материалу find_name(string) find_mat(string)
Чуть ниже находится раздел Goal, в котором содержатся предикаты, вызывающиеся при запуске программы:
Goal do_mbase. % вызов предиката do_mbase
Ну и наконец Clauses. Здесь я опишу все правила программы.
repeat — необходимо для зацикливания. Реализуется вызовом самого себя:
repeat. repeat:-repeat.
read_prov — при вводе целого числа возвращает это число, иначе вызывает предикат error:
read_prov(Vibor):- readint(Vibor); error, Vibor = 0, menu.
Меню

do_mbase — предикат, с которого начинается выполнение программы. Создаёт главное окно и вызывает menu.
menu — создаёт меню и ждет пока пользователь введет число от 1 до 9. При вводе числа вызывается предикат process, который выполняет одну из девяти выбранных функций:
do_mbase :- makewindow(1,11,3," T-SHIRTS DATABASE ",0,0,25,80), menu, clear_database. menu :- repeat, clearwindow, nl, write(" ********************************* "),nl, write(" * 1. Read Database from file * "),nl, write(" * 2. Add new T-shirt in DB * "),nl, write(" * 3. Delete T-shirt from DB * "),nl, write(" * 4. Edit T-shirt in DB * "),nl, write(" * 5. Find T-shirt * "),nl, write(" * 6. Show all data * "),nl, write(" * 7. Write Database to file * "),nl, write(" * 8. Delete All DB * "),nl, write(" * 9. Exit * "),nl, write(" ********************************* "),nl, write(" Choose number 1-9 : "), read_prov(Vibor),nl, process(Vibor),Vibor = 9.
Далее опишем все эти функции.
Описание функций
Считывание базы данных из csv файла

process(1) — создаёт окно, где можно ввести имя csv файла из которого мы хотим считать базу данных. Считывание происходит за счет предиката read_rows(). При успешном считывании БД выводится сообщение «DB successfully read from file.». При неудаче выводится «Error reading file!». Затем, в обоих случаях, после нажатия пробела, происходит переход обратно в меню.
process(1) :- makewindow(2,11,3,"Read data from file",2,20,15,40),shiftwindow(2), write("Input File name (data.csv): "), readln(Filename), existfile(Filename), openread(datafile, Filename), readdevice(datafile), read_rows(), closefile(datafile), readdevice(keyboard), write("DB successfully read from file."),nl,!, write("Press space bar"), readchar(_), removewindow, shiftwindow(1), clearwindow, menu; write("Error reading file!"), nl, !, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu, fail.
read_rows() — построчно считывает csv файл, при помощи восьми вызовов (у нас 8 параметров в базе данных) предиката front_string. После считывания каждой строки, вставляет полученные значения в конец базы данных dt_shirt, используя встроенный предикат assertz.
front_string(Line, Param, Tail) — считывает строку, пока не встретит csv разделитель ‑ ;
Имеет 3 параметра:
-
Line — начальная строка
-
Param — часть строки до первого разделителя ‑ ;
-
Tail — оставшаяся строка после разделителя ‑ ;
read_rows() :-not(eof(datafile)), readln(Line), front_string(Line, F1_STR, Tail1), front_string(Tail1, F2_STR, Tail2), str_int(F2_STR, Price), front_string(Tail2, F3_STR, Tail3), front_string(Tail3, F4_STR, Tail4), front_string(Tail4, F5_STR, Tail5), str_int(F5_STR, Europe), front_string(Tail5, F6_STR, Tail6), front_string(Tail6, F7_STR, Tail7), front_string(Tail7, F8_STR, _), str_int(F8_STR, Year), assertz(dt_shirt(F1_STR,Price,F3_STR,F4_STR,Europe,F6_STR,F7_STR,Year)), !, read_rows(); not(eof(datafile)), !, write(" ********************************"), nl, write(" * READING ERROR! * "), nl, write(" * REMAINING DATA WAS NOT READ! * "), nl, write(" * SOME MATERIALS ADDED! * "), nl, write(" ******************************** "), nl; !. front_string("", "", "") :- !. front_string(Line, Param, Tail) :-frontchar(Line, LineH, LineT), LineH = ';', !, Param = "", Tail = LineT; frontchar(Line, LineH, LineT), LineH <> ';', !, front_string(LineT, T, Tail), str_char(LineHS, LineH), concat(LineHS, T, Param).
Добавление новой футболки в базу данных

process(2) — создаёт окно, где можно ввести параметры новой футболки. Ввод числовых значений происходит через предикат read_until_not_integer. После ввода всех параметров, они вставляются в конец базы данных dt_shirt, используя встроенный предикат assertz.
process(2) :- makewindow(3,11,3,"Add data",2,20,18,58),shiftwindow(3), write("Please, Input T-shirt:"),nl, write("Name: "), readln(Name), write("Price (RUB): "), read_until_not_integer(Price), write("Sex: "), readln(Sex), write("Size (International) : "), readln(World), write("Size (Europe): "), read_until_not_integer(Europe), write("Color: "), readln(Color), write("Material: "), readln(Material), write("Production year: "), read_until_not_integer(Year), assertz(dt_shirt(Name,Price,Sex,World,Europe,Color, Material,Year)), write(Name," added to DB"), nl,!, write("Press space bar. "), readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
read_until_not_integer — проверяет, является ли введенное значение целым числом больше 0 или нет. Если не является, то вызывается повторно.
read_until_not_integer(Integer):- readint(Integer), Integer >=0, !; write("Enter integer number >=0: "), read_until_not_integer(Integer).
Удаление футболки из базы данных

process(3) — создаёт окно, где можно ввести название футболки, которую необходимо удалить. После ввода названия футболки, она удаляется из базы данных dt_shirt, используя встроенный предикат retract.
process(3) :- makewindow(4,11,3,"Delete data",10,30,7,40),shiftwindow(4), write("Input T-shirt name: "), readln(Name), retract(dt_shirt(Name,_,_,_,_,_,_,_)), write(Name," removed from DB "), nl, !, write("Press space bar."), readchar(_), removewindow, shiftwindow(1); write("No data."),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1).
Изменение информации о футболке

process(4) — создаёт окно, где можно ввести название футболки, информацию о которой необходимо изменить. После ввода названия футболки, она удаляется из базы данных dt_shirt, используя встроенный предикат retract. Далее вводятся все парам
process(4) :- makewindow(5,11,3,"Edit data",2,20,18,58),shiftwindow(5), write("Input T-shirt name: "), readln(Name1), retract(dt_shirt(Name1,_,_,_,_,_,_,_)), write("Name: "), readln(Name), write("Price (RUB): "), read_until_not_integer(Price), write("Sex: "), readln(Sex), write("Size (International) : "), readln(World), write("Size (Europe): "), read_until_not_integer(Europe), write("Color: "), readln(Color), write("Material: "), readln(Material), write("Production year: "), read_until_not_integer(Year), assertz(dt_shirt(Name,Price,Sex,World,Europe,Color, Material,Year)),nl, !, write("Press space bar."), readchar(_), removewindow, shiftwindow(1); write("No data."),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
Показать всю информацию о футболке


process(5) — создаёт окно, где можно выбрать, как искать нужную футболку: 1 — по названию или 2 — по материалу. После вызывается предикат find, в котором происходит поиск футболки по выбранному нами параметру и затем выводится вся информация о ней.
process(5) :- makewindow(6,11,3," Show T-shirt ", 2,30,22,47), shiftwindow(6), write("1. Find T-shirt by Name "),nl, write("2. Find T-shirt by Material "),nl, write(" Choose number 1-2 : "), read_until_not_integer(N), N>0,N<3, find(N), write("Press space bar"), readchar(_), removewindow, shiftwindow(1), clearwindow, menu; write("Wrong input."),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
find(1) — необходимо ввести название футболки, информацию о которой мы хотим получить. После ввода названия, вызывается предикат find_shirt_name, осуществляющий поиск футболки в базе данных. Если такое название было найдено, в консоль выводится вся информация о футболке. Если нет, то выводится сообщение «No such T-shirt in database!».
find(2) — аналогично find(1), только теперь необходимо ввести материал интересующей нас футболки.
find(1):-clearwindow, write("Input T-shirt name: "), readln(Name), find_shirt_name(Name), find_name(Name). find(1):-write("No such T-shirt in database!"). find(2):-clearwindow, write("Input T-shirt material: "), readln(Material), find_material(Material), find_mat(Material). find(2):-write("No such T-shirt in database!"). find(_):-write("Error "). find_shirt_name(Name):- dt_shirt(Name,Price,Sex,World,Europe,Color, Material,Year),nl, write(" Name : ",Name),nl, write(" Price (RUB) : ",Price),nl, write(" Sex : ",Sex),nl, write(" Size (International): ",World),nl, write(" Size (Europe): : ",Europe), nl, write(" Color : ",Color),nl, write(" Material : ",Material),nl, write(" Production year : ",Year),nl, nl, fail. find_shirt_name(_). find_material(Material):- dt_shirt(Name,Price,Sex,World,Europe,Color, Material,Year),nl, write(" Name : ",Name),nl, write(" Price (RUB) : ",Price),nl, write(" Sex : ",Sex),nl, write(" Size (International): ",World),nl, write(" Size (Europe): : ",Europe), nl, write(" Color : ",Color),nl, write(" Material : ",Material),nl, write(" Production year : ",Year),nl, nl, fail. find_material(_). find_name(Name):-dt_shirt(Name,_,_,_,_,_,_,_). find_mat(Material):-dt_shirt(_,_,_,_,_,_,Material,_).
Показать все записи в базе данных

process(6) — создаёт окно, в котором выводятся все записи базы данных на экран. Для этого используется предикат write_all.
process(6) :- makewindow(7,11,3," Show All data ", 0,0,25,80), shiftwindow(7), write("Name, Price(Rub), Sex, Size(Inter.), Size(Europe), Color, Material, Year"), nl, write("************************************************************************"), nl, write_all, nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu; write("No data."),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
write_all() — использует предикат findall(X,P,L), который собирает в список L все объекты X, удовлетворяющие цели P.
write_all([P1|T1], [P2|T2], [P3|T3], [P4|T4], [P5|T5], [P6|T6], [P7|T7], [P8|T8]) — выводит все значения найденные в базе данных через запятую.
write_all() :- findall(P1, dt_shirt(P1,_,_, _,_,_,_,_), P1s), findall(P2, dt_shirt(_,P2,_,_,_,_,_,_ ), P2s), findall(P3, dt_shirt(_,_,P3, _,_,_,_,_), P3s), findall(P4, dt_shirt(_,_,_,P4,_,_,_,_ ), P4s), findall(P5, dt_shirt(_,_,_,_,P5,_,_,_ ), P5s), findall(P6, dt_shirt(_,_,_,_,_,P6,_,_ ), P6s), findall(P7, dt_shirt(_,_,_, _,_,_,P7,_), P7s), findall(P8, dt_shirt(_,_,_, _,_,_,_,P8), P8s), write_all(P1s, P2s, P3s, P4s, P5s, P6s, P7s, P8s); writedevice(screen). write_all([], [], [], [], [], [], [], []) :- !. write_all([P1|T1], [P2|T2], [P3|T3], [P4|T4], [P5|T5], [P6|T6], [P7|T7], [P8|T8]) :- write(P1,", ", P2," (RUB), ", P3,", ", P4,", ", P5,", ", P6,", ", P7,", ", P8),nl, write("------------------------------------------------------------------------"),nl, write_all(T1, T2, T3, T4, T5, T6, T7, T8).
Записать базу данных в файл csv


process(7) — создаёт окно, в котором необходимо ввести название файла для сохранения базы данных. После записывающее устройство ставится на файл — writedevice(datafile) и вызывается предикат write_all_csv, записывающий базу данных в файл.
process(7) :- makewindow(8,11,3," Write Database to file ", 7,30,12,47), shiftwindow(8), write("Input file name (data.csv): "), readln(Filename), existfile(Filename), % Существует ли файл openwrite(datafile, Filename), writedevice(datafile), write_all_csv, closefile(datafile), writedevice(screen), write("DB successfully written to file."), nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu; write("Error writing file!"),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
write_all_csv() — устроен аналогично write_all(), только вместо запятых, параметры футболки разделяются символом — ;. Это необходимо для корректной записи в csv файл.
write_all_csv() :- findall(P1, dt_shirt(P1,_,_, _,_,_,_,_), P1s), findall(P2, dt_shirt(_,P2,_,_,_,_,_,_ ), P2s), findall(P3, dt_shirt(_,_,P3, _,_,_,_,_), P3s), findall(P4, dt_shirt(_,_,_,P4,_,_,_,_ ), P4s), findall(P5, dt_shirt(_,_,_,_,P5,_,_,_ ), P5s), findall(P6, dt_shirt(_,_,_,_,_,P6,_,_ ), P6s), findall(P7, dt_shirt(_,_,_, _,_,_,P7,_), P7s), findall(P8, dt_shirt(_,_,_, _,_,_,_,P8), P8s), write_all_csv(P1s, P2s, P3s, P4s, P5s, P6s, P7s, P8s); writedevice(screen). write_all_csv([], [], [], [], [], [], [], []) :- !. write_all_csv([P1|T1], [P2|T2], [P3|T3], [P4|T4], [P5|T5], [P6|T6], [P7|T7], [P8|T8]) :- write(P1,"; ", P2,"; ", P3,"; ", P4,"; ", P5,"; ", P6,"; ", P7,"; ", P8),nl, write_all_csv(T1, T2, T3, T4, T5, T6, T7, T8).
Удаление всех записей из базы данных

process(8) — создаёт окно, в котором при успешном удалении всех записей из базы данных, выведется сообщение «DB has been cleared». При ошибке выведется «Error writing file!».
process(8) :- makewindow(9,11,3," Delete All DB ",10,30,7,40), shiftwindow(9), clear_database, write("DB has been cleared"), nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu; write("Error writing file!"),nl,!, write("Press space bar."),readchar(_), removewindow, shiftwindow(1), clearwindow, menu.
clear_database — удаляет все факты из базы данных, используя встроенный предикат retract:
clear_database:- retract(dt_shirt(_,_,_,_,_,_,_,_)), fail. clear_database:-!.
Выход из программы

process(9) — очищает базу данных и выводит сообщение «See you again! «.
process(9) :- clear_database, write("See you again! "),readchar(_),exit.
Конец
Вот вроде и все.
Надеюсь вы нашли что искали)
Ссылка на исходный код: https://github.com/KirillTaE/Dynamic_DataBase_on_TurboProlog
ссылка на оригинал статьи https://habr.com/ru/post/716012/
Добавить комментарий