Теория

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

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

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

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

Комментарии (12)


  1. MAXH0
    00.00.0000 00:00
    +2

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


    1. KirillTaE Автор
      00.00.0000 00:00
      +1

      Ну а мне с программой моего ВУЗа пришлось его осилить) Изучал данный язык в 2022 году....


      1. MAXH0
        00.00.0000 00:00

        Интересно! Экспертные системы?


        1. KirillTaE Автор
          00.00.0000 00:00
          +2

          Предмет - Логическое и функциональное программирование. Направление - Программная инженерия


      1. artyomsoft
        00.00.0000 00:00
        +1

        Вспомнились 90е, институт. Кроме Пролога мы еще один экзотический язык изучали под названием GPSS

        Я понимаю ностальгию вашего преподавателя по тем временам. Но сейчас есть более современный SWI Prolog


        1. torbasow
          00.00.0000 00:00

          1990-е, да. Мы официально не изучали, но было интересно, я изучил сам, и сдавал на нём курсовую.


  1. 18741878
    00.00.0000 00:00
    +4

    Судя по картинкам, автор использует Turbo Prolog года этак 1993/1994 (плюс-минус пара лет). На чем запускали? Неужели сохранился компьютер тех лет с MS-DOS?


    1. KirillTaE Автор
      00.00.0000 00:00
      +2

      Для запуска сего чуда использовал эмулятор DOS - DOSBox.


      1. 18741878
        00.00.0000 00:00
        +2


        1. speshuric
          00.00.0000 00:00
          +1

          Книга напомнила интересный опыт на Прологе. Год этак 2000-2001 или около того. Конец весны. Зашёл в выходной к знакомому в общежитии (жили на соседних этажах, совершенно небогато и по студенчески), а он в печали. Я спросил его, чего он такой невесёлый, на что он ответил, что надо сдавать курсовую и зачёт по Прологу, а он его не понимает. Я Пролог понимаю, но понимаю, что понять пролог за выходные э... хм... сложновато. Но мне заняться особо было нечем в тот день и я сказал:
          -- Окей, давай помогу. Но тут без поллитры не разобраться.
          Молча знакомый достаёт бутылку водки.
          -- А закусить есть чем?
          Достаёт кубик бульона.
          -- Только это.
          -- А хоть запить?
          -- Есть вода в чайнике.
          ...
          Но разобрались.


  1. heiheshang
    00.00.0000 00:00
    +2

    Обычно программа на Прологе состоит из четырех основных программных >>разделов. К ним относятся:
    Я бы так про Пролог не писал - это касается Turbo Prolog, VIP-Prolog конкретных реализаций пролога, обычная прграмма на прологе состоит из предикатов. Посмотрите на SWI-Prolog. Прочитал статью прям в юность вернулся, столько лет прошло, а я до сих пор на прологе пишу.


  1. mkgs210
    00.00.0000 00:00
    +1

    Спасибо большое! Тоже изучаю этот предмет в вузе.