Рассмотрим, как нужно объявлять секции database и как можно изменить содержание внутренней базы данных.
Внутренняя база данных состоит из фактов, которые можно непосредственно добавлять и удалять из вашей программы на Прологе во время ее исполнения. Можно объявлять предикаты, описывающие внутреную базу данных в секции database вашей программы и использовать эти предикаты таким же образом, как используются предикаты, описанные в секции predicates.
Для добавления новых фактов в базу данных в Прологе используются предикаты asserta и assertz, а предикаты retract и retractall служат для удаления существующих фактов. Можно изменить содержание базы данных, сначала удалив факт, а потом вставив новую версию этого факта (или совершенно другой факт). Предикат consult считывает факты из файла и добавляет их к внутренней базе данных, а save сохраняет содержимое внутренней секции database в файле.
Так как Пролог реализует реляционную базу данных, как совокупность фактов, его можно использовать как мощный язык запросов к внутренней базе данных. Стандартный алгоритм Пролога автоматически выбирает факты с правильными значениями известных параметров и присваивает значения любым неизвестным параметрам, пока алгоритм поиска с возвратом на даст все решения для данного запроса.
Пролог интерпретирует факты, принадлежащим к базе данных, таким же образом, как обычные предикаты. Факты предикатов внутренней базы данных хранятся в таблице, которые можно легко изменять, тогда как обычные предикаты для достижения максимальной скорости компилируются в двоичный код.
Ключевое слово database определяет начало последовательности объявлений предикатов, описывающих внутреннюю базу данных. Во время выполнения с клавиатуры можно с помощью предикатов asserta и assertz добавлять факты (но не правила) в базу данных. Вызвав стандартный предикат consult, Можно извлечь добавляемые факты из файла на диске. Секция database может выглядеть так же, как в следующем примере:
domains
name, address = string
age = integer
gender = male; female
database
person(name,address,age,gender)
predicates
male(name,address,age)
female(name,address,age)
child(name,age,gender)
clauses
male(Name,Address,Age):- person(Name,Address,Age,male).
…
В этом примере Можно использовать предикат person таким же образом, как используются другие предикаты (male,female,child). Единственное отличие состоит в том, что Можно добавлять и удалять факты для предиката person во время работы программы.
Следует отметить следующее:
1. Можно добавлять в базу данных только факты, а не правила.
2. Факты базы не могут содержать свободных переменных.
Допускается наличие нескольких секций database, но для этого нужно явно указать имя каждой секции database. Это требуется потому, что consult и save могут загружать и сохранять определенную секцию database по имени.
database — mydatabase
myFirstRelation(integer)
mySecondRelation(real, string)
myThirdRelation(string)
/* и т.д. */
goal
consult(«example.dba»,mydatabase),
retract(myFirstRelation(1)),
asserta(myFirstRelation(0)),
save(«example.dba»,mydatabase).
Такое описание создает базу данных с именем mydatabase. Если не дать имени для внутренней базы данных, то по умолчанию ей присваивается стандартное имя dbasedom
Важно понимать, что имена предикатов базы данных в модуле являются уникальными. В двух различных секциях database нельзя использовать одинаковые имена предикатов.
Для того, чтобы ввести в программу факты, существует три основных способа:
— включение фактов в состав вашей секции clauses;
— во время выполнения программы с помощью пердикатов asserta и assertz;
— путем загрузки файла с фактами базы данных с помощью предиката consult.
Все стандартные предикаты Пролога: asserta, assertz, retract, retractall, consult и save могут иметь один или два агрумента. Необязательный второй аргумент представляет собой имя внутренней базы данных. Опишем эти предикаты далее. Обозначение «/1» и «/2» после каждого имени предиката указывает необходимое число аргументов для данной версии предиката. Комментарии после форматов (такие как /* (i) */ и /* (o,i) */ ) показывают поток параметров для этого предиката.
Предикат asserta вставляет новый факт в базу данных перед имеющимися фактами для данного предиката, а assertz вставляет факты после данного предиката.
Добавление фактов имеет следующий формат:
asserta(<факт>) /* (i) */
asserta(<факт>,databaseName) /* (i,i) */
assertz(<факт>) /* (i) */
assertz(<факт>,databaseName) /* (i,i) */
Поскольку имена предикатов базы данных уникальны внутри программы или модуля, для предикатов asserta и assertz всегда известно, куда нужно добавлять факт. Однако, для того, чтобы обеспечить работу с правильной базой данных, в целях проверки типа можно использовать необязательный второй аргумент.
Использование предиката assert имеет действие аналогичное использованию assertz.
Первый предикат следующего примера вставит факт о Suzanne, описанный предикатом person, после всех фактов person, хранящихся в текущий момент в памяти. Второй вставит факт о Michael перед всеми имеющимися фактами предиката person. Третий — вставит факт о John после всех других фактов likes в базе данных likesDatabase, а четвертый — вставит факт о Shannon в той же базе данных перед всеми другими фактами likes.
assertz(person(«Suzanne», «New Haven», 35)).
asserta(person(«Michael», «New York», 26)).
assertz(likes(«John», «money»),likesDatabase).
asserta(likes(«Shannon», «hard work»),likesDatabase).
После вызова этих предикатов база данных в памяти с произвольным обращением будет выглядеть так, как будто вы начали работу со следующими фактами:
/* Внутренняя база данных — dbasedom */
person(«Michael», «New York», 26).
/*… другие факты person… */
person(«Suzanne», «New Haven», 35).
/* Внутренняя база данных — likesDatabase */
likes(«John», «money»)
/*… другие факты likes… */
likes(«Shannon», «hard work»)
Предикат retract удаляет первый факт в вашей базе данных, который совпадает с фактом <факт>. Этот предикат является неопределенным. При поиске с возвратом предикат retract возвращает альтернативные решения и удаляет все совпадающие факты, пока они имеются, после чего он не находит нужных фактов. Предикат retract имеет следующий формат:
retract(<факт>[, databaseName]) /* (i,i) */
Предположим в вашей программе имеются следующие секции database:
database
person(string, string, integer)
database — likesDatabase
likes(string, string)
dislikes(string, string)
clauses
/* база данных */
person(«Fred», «Capitola»,35)
person(«Fred», «Omaha»,37)
person(«Michael», «Brooklin»,26)
/* база данных — likesDatabase */
likes(«John», «money»)
likes(«Jane», «money»)
likes(«Chris», «chocolate»)
likes(«John», «broccoli»)
dislikes(«Fred», «broccoli»).
dislikes(«Michael», «beer»).
Имея такие секции database, Прологу можно задать следующие цели:
retract(person(«Fred», _, _)). /* 1 */
retract(likes( _,»broccoli»)). /* 2 */
retract(likes( _, «money»),likesDatabase). /* 3 */
retract(person(«Fred», _, _),likesDatabase). /* 4 */
Первая цель удалит первый факт person о Fred из базы данных dbasedom (имя которой задается по умолчанию). С помощью второй цели из базы данных likesDatabase будет удален первый факт, совпадающий с likes(X,»broccoli»). При этом, Пролог знает из какой базы производить удаление, поскольку имена предикатов базы данных уникальны: предикат person находится только в неименованной базе данных, а likes только в базе likesDatabase.
Третья и четвертая цель показывают, как Можно использовать для проверки типа второй аргумент. Третья цель успешно реализуется, удаляя первый факт, совпадающий с likes(,»money») из likesDatabase, а четвертая цель выдаст ошибку, потому что нет (и не может быть) факта person в базе данных likesDatabase. Сообщение об ошибке выглядит следующим образом:
506 Type Error: The functor does not belong to the domain (функтор не относится к даннму домену)
Следующая цель иллюстрирует, как Можнополучить значения из предиката retract:
goal
retract(person(Name, Age)),
write(Name, «, «, Age),
fail.
Можнотакже удалить все факты из заданной секции database с помощью предиката retract. Если при вызове retract в качестве первого аргумента будет указана свободная переменная, то чтобы определить, из какой секции database нужно произвести удаление, он будет анализировать второй аргумент. Например:
goal
retract(X, mydatabase),
write(X),
fail.
Предикат retractall удаляет из базы данных все факты, совпадающие с образцом <факт>. Этот предикат всегда завершается успешно. Из retractall выходные значения получить нельзя. Это означает, что как и в случае not для неограничеваемых переменных (т.е. переменных, значения которым еще не присвоены) нужно использовать символ подчеркивания.
retractall имеет следующий формат:
retractall(<факт>[, databaseName])
Также как в случае предикатов assert и retract для проверки типа можно использовать второй аргумент. И, как в случае предиката retract, если при вызове retractall используется символ подчеркивания, то из указанной секции database можно удалить все факты.
Следующая цель удаляет все факты о мужчинах из базы данных с фактами person:
retractall(person(,, male, )).
Следующая цель удаляет все факты из базы mydatabase.
retractall(, mydatabase).
Предикат consult считывает из файла (fileName) факты, описанные в секции database, и вставляет их в вашу программу в конец соответствующей базы данных (аналогично тому, как предикат assertz включает факты.) Предикат consult имеет один или два аргумента:
consult(fileName) /* (i) */
consult(fileName, databaseName) /* (i,i) */
Однако, в отличии от assertz, если вы вызовите consult только с одним аргументом (без имени базы данных), то будут считаны только факты, которые были описаны в секции без имени (по умолчанию dbasedom).
Если вы вызовите consult с двумя аргументами (имя файла и имя базы данных), то будут только проверены факты из указанной базы данных. Если файл содержит что-нибудь кроме фактов указанной базы, то предикат consult, когда он дойдет до этого, возвратит ошибку.
Обратите внимание, что предикат consult считывает по одному факту. Если файл содержит десять фактов, а в седьмом факте имеется какая-нибудь синтаксическая ошибка, consult занесет шесть первых фактов в базу данных после чего выдаст сообщение об ошибке.
Отметим, что предикат consult может считывать файлы только в том формате, который создает save (для включения фактов с максимально возможной скоростью). Файлы не должны содержать:
— прописных символов;
— пробелов за исключением тех, которые содержатся внутри
строк в двойных кавычках;
— комментариев;
— пустых строк;
— символов без двойных кавычек.
При создании или изменении файла с фактами в редакторе нужно соблюдать аккуратность.
Предикат save сохраняет факты из указанной базы данных в файле. Этот предикат имеет один или два аргумента:
save(fileName) /* (i) */
save(fileName, databaseName) /* (i,i) */
При вызове предиката save только с одним аргументом (без имени базы данных), в файле fileName будут сохранены факты из базы данных dbasedom, используемой по умолчанию.
При вызове предиката save с двумя аргументами (имя файла и имя базы данных), в указанном файле будут сохранены факты из database секции databaseName.
