Динамическая База данных на Turbo Prolog

Теория

Обычно программа на Прологе состоит из четырех основных программных разделов. К ним относятся:

  • раздел 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

Описание программы

База данных будет содержать одну таблицу — таблицу футболок, столбцы которой перечислены далее:

  1. Name — название.

  2. Price (RUB) — цена в рублях.

  3. Sex — мужская или женская футболка.

  4. Size (International) — международный размер футболки.

  5. Size (Europe) — европейский размер футболки.

  6. Color — цвет.

  7. Material — материал футболки.

  8. Production year — год производства.

Предполагаются следующие возможности программы:

  1. Считывание базы данных из CSV-файла

  2. Добавление новой записи в базу данных;

  3. Удаление футболки по названию;

  4. Изменение записи в базе данных;

  5. Поиск футболки по названию;

  6. Отображение всех футболок в консоли;

  7. Сохранение базы данных в CSV-файл;

  8. Удаление всех записей из базы данных;

  9. Выход из программы;

Код

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

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 файла

Считывание базы данных из csv файлаСчитывание базы данных из 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 параметра:

  1. Line — начальная строка

  2. Param — часть строки до первого разделителя ‑ ;

  3. 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).

Изменение информации о футболке

523d150c0007432738cae2028c56e027.png

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.

Показать всю информацию о футболке

1-искать по названию; 2-искать по материалу1-искать по названию; 2-искать по материалуПоиск по названию (слева);                                                Поиск по материалу (справа)Поиск по названию (слева); Поиск по материалу (справа)

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

Записать базу данных в файл csvЗаписать базу данных в файл csvБаза данных в CSV файлеБаза данных в 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

© Habrahabr.ru