После приведения формулы вида ПНФ к виду ССФ вы получаете формулу, в безквантовой матрице которой дизъюнкт содержит оба контранрных атома:. Как тогда проводить его унификацию, если в случае замены x на f(x) весь дизъюнкт обратится в единицу? |
Внутренние (динамические) базы данных
Пример. Напишем программу, реализующую компьютерный вариант телефонного справочника. Основное назначение этой не очень сложной программы — находить по фамилии человека его телефонный номер или, наоборот, по телефонному номеру — фамилию владельца телефона. У пользователя нашей программы должна быть возможность добавлять информацию в базу данных, а также удалять и изменять устаревшую информацию.
Приступим к реализации нашего проекта. Внутренняя база данных будет содержать факты, описывающие единственный предикат, имеющий два аргумента. Первым аргументом предиката будет фамилия человека, а вторым — его телефонный номер. Для упрощения программы будем считать, что соответствие между фамилиями и номерами телефонов взаимооднозначное, то есть каждой фамилии соответствует не более одного телефонного номера, и наоборот.
Сделаем так, чтобы при запуске программы появлялось меню, из которого пользователь мог выбрать, какое действие с телефонной базой он хотел бы осуществить. Реализуем пять операций:
- Получение информации о телефонном номере по фамилии человека.
- Получение информации о фамилии абонента по телефонному номеру.
- Добавление новой записи в телефонную базу.
- Изменение существующей в телефонной базе записи.
- Удаление записи из телефонной базы.
Нужно учесть, что пользователь может ошибиться и нажать клавишу, не соответствующую ни одной из пяти указанных операций. После выполнения каждой из операций программа должна вернуться обратно в меню, чтобы у пользователя не было необходимости запускать программу заново, если ему нужно выполнить еще одно действие.
Кроме того, у пользователя должна быть возможность выйти из программы, не совершая никаких действий. При выходе из программы факты телефонной базы должны быть сохранены из оперативной памяти в файл на диске, а оперативная память очищена от ненужных фактов.
Эти действия выполняет следующее правило (символ '0' означает, что пользователь нажал соответствующую клавишу):
m('0'):– save("phones.ddb "), /* сохраняем телефонную базу в файл */ retractall(_)./* удаляем все факты из внутренней базы данных */
В начале работы программы факты из телефонной базы, хранящейся в файле на диске, должны загружаться во внутреннюю базу данных, в случае, если такой файл существует.
Предикат, предназначенный для выполнения этих действий, выглядит следующим образом:
start:– existfile("phones.ddb"),!, /* если существует файл с телефонной базой */ consult("phones.ddb "), /* , то загружаем факты во внутреннюю базу данных */ menu. /* и вызываем меню */ start:– menu. /* если такого файла еще нет, просто вызываем меню */
Если пользователь выбрал первую операцию, должен быть выдан телефонный номер абонента (если в телефонной базе имеется соответствующий факт) или сообщение о том, что в телефонной базе нет такой информации.
Это реализуют два приведенных ниже предиката.
m('1'):– write("Введите фамилию"), nl, /* выводим приглашение ввести фамилию */ readln(Name), /* читаем введенную фамилию в переменную Name */ name_phone(Name, Phone), /* вызываем предикат, который помещает в переменную Phone телефонный номер, соответствующий фамилии Name или сообщение об отсутствии информации */ write("Номер телефона: ",Phone), /* выводим значение переменной Phone */ readchar(_), /* ждем нажатия любой клавиши */ menu. /* возвращаемся в меню */ name_phone(Name,Phone):– phone(Name,Phone),!. name_phone(_,"Нет информации о телефонном номере"). /* если нужного факта во внутренней базе данных не нашлось, то вместо телефонного номера возвращаем соответствующее сообщение */
Если пользователь желает выполнить вторую операцию, то должна быть выведена фамилия абонента, если в нашей телефонной базе имеется соответствующий факт. Иначе выводится сообщение о том, что у нас нет такой информации.
Соответствующие предикаты будут выглядеть следующим образом:
m('2'):– write("Введите номер телефона"),nl, readln(Phone), phone_name(Name, Phone), write("Фамилия абонента: ",Name), readchar(_), menu. /* вызываем меню */ phone_name(Name,Phone):– phone(Name,Phone). phone_name("Нет информации о владельце телефона",_). /* если нужного факта во внутренней базе данных не нашлось, то вместо фамилии абонента возвращаем соответствующее сообщение */
Если пользователем была выбрана третья операция, то нужно дать ему возможность ввести фамилию и номер абонента, после чего добавить соответствующий факт в базу данных.
Это будет выглядеть следующим образом:
m('3'):– write("Введите фамилию"),nl, readln(Name), write("Введите номер телефона"),nl, readln(Phone), assert(phone(Name,Phone)), /* добавляем факт во внутреннюю базу данных */ menu. /* вызываем меню */
Если пользователь желает выполнить четвертую операцию, то нужно дать ему возможность ввести фамилию абонента и его новый телефонный номер, после чего удалить устаревшую информацию из телефонной базы (с помощью предиката retract ) и добавить туда новую информацию (используя встроенный предикат assert ).
Соответствующее этим рассуждениям предложение:
m('4'):– clearwindow, write("Введите фамилию"),nl, readln(Name), write("Введите новый номер телефона"),nl, readln(Phone), retract(phone(Name,_)), /* удаляем устаревшую информацию из внутренней базы данных */ assert(phone(Name,Phone)), /* добавляем новую информацию в телефонную базу */ menu. /* вызываем меню */
Если пользователем была выбрана пятая операция, то нужно узнать у него, например, номер (или фамилию) абонента, после чего удалить соответствующую информацию из внутренней базы данных, воспользовавшись предикатом retract.
Запишем это предложение:
m('5'):– write("Укажите номер телефона, запись о котором нужно удалить из телефонной базы"), nl, readln(Phone), retract(phone(_,Phone)), /* удаляем соответствующий факт из внутренней базы данных */ menu. /* вызываем меню */
Приведем полный текст программы.
DOMAINS /* раздел описания доменов */ name, number = String /* фамилию абонента и телефонный номер будем хранить в виде строк */ file=f /* файловый домен будем использовать для считывания с диска и записи на диск нашей телефонной базы */ DATABASE /* раздел описания предикатов внутренней базы данных */ phone(name, number) PREDICATES /* раздел описания предикатов */ name_phone(name, number) /* этот предикат находит номер телефона по фамилии абонента */ phone_name(name, number) /* этот предикат находит фамилию абонента по номеру телефона */ m(char) /* этот предикат реализует выполнение соответствующего пункта меню */ menu /* этот предикат реализует вывод меню и обработку выбора пользователя */ start /* этот предикат проверяет наличие файла с телефонной базой на диске и либо загружает факты из нее во внутреннюю базу данных, если такой файл существует, либо создает этот файл, если его не было */ CLAUSES /* раздел описания предложений */ name_phone(Name,Phone):– phone(Name,Phone),!. name_phone(_,"Нет информации о телефонном номере"). /* если соответствующего факта во внутренней базе данных не нашлось, вместо телефонного номера возвращаем соответствующее сообщение */ phone_name(Name,Phone):– phone(Name,Phone). phone_name("Нет информации о владельце телефона",_). /* если соответствующего факта во внутренней базе данных не нашлось, вместо фамилии абонента возвращаем соответствующее сообщение */ menu:– clearwindow, /* очистка текущего окна */ write("1– Получение телефонного номера по фамилии "),nl, write("2 — Получение фамилии абонента по номеру телефона "),nl, write("3 — Добавление новой записи в телефонную базу."),nl, write("4 — Изменение номера абонента"),nl, write("5 — Удаление записи из телефонной базы"),nl, write("0 — Выйти"),nl, readchar(C), /* читаем символ с клавиатуры */ m(C). /* вызываем выполнение соответствующего пункта меню */ m('1'):– clearwindow, write("Введите фамилию"), nl, readln(Name), name_phone(Name, Phone), write("Номер телефона: ",Phone), readchar(_), menu. m('2'):– clearwindow, write("Введите номер телефона"),nl, readln(Phone), phone_name(Name, Phone), write("Фамилия абонента: ",Name), readchar(_), menu. m('3'):– clearwindow, write("Введите фамилию"),nl, readln(Name), write("Введите номер телефона"),nl, readln(Phone), assert(phone(Name,Phone)), /* добавляем факт во внутреннюю базу данных */ menu. m('4'):– clearwindow, write("Введите фамилию"),nl, readln(Name), write("Введите новый номер телефона"),nl, readln(Phone), retract(phone(Name,_)), /* удаляем устаревшую информацию из внутренней базы данных */ assert(phone(Name,Phone)), /* добавляем новую информацию в телефонную базу */ menu. m('5'):– clearwindow, write("Укажите номер телефона, запись о котором нужно удалить из телефонной базы"), nl, readln(Phone), retract(phone(_,Phone)), /* удаляем соответствующий факт из внутренней базы данных */ menu. m('0'):– save("phones.ddb "), /* сохраняем телефонную базу в файл */ retractall(_)./* удаляем все факты из внутренней базы данных */ m(_):– menu. /* если пользователь по ошибке нажал клавишу, отличную от тех, реакция на которые предусмотрена, ничего плохого не произойдет, будет отображено меню еще раз */ start:– existfile("phones.ddb"),!, /* если файл с телефонной базой существует */ consult("phones.ddb "), /* загружаем факты во внутреннюю базу данных */ menu. /* вызываем меню */ start:– openwrite(f,"phones.ddb"), /* если файла с телефонной базой не существует, создаем его */ closefile(f), menu. /* вызываем меню */ GOAL /* раздел внутренней цели*/ StartЛистинг 13.1. Программа, реализующая компьютерный вариант телефонного справочника.