Шаг 62.
Основы логического программирования.
Обновление внутренней базы фактов

    На этом шаге мы рассмотрим обновление внутренней базы фактов.

    Факты для предикатов базы фактов могут быть определены во время компиляции в разделе clauses, как это показано в последнем примере. Во время выполнения факты могут быть добавлены и удалены, используя описанные ниже предикаты. Обратите внимание, что факты, определенные во время компиляции в разделе clauses, также могут быть удалены, они ничем не отличаются от фактов, добавленных во время выполнения.

    Стандартные предикаты Пролога для работы с фактами: assert, asserta, assertz, retract, retractall, consult и save - могут иметь один или два аргумента. Необязательный второй аргумент представляет собой имя внутренней базы фактов. Обозначение /1 и /2 после каждого имени предиката указывает необходимое число аргументов для данной версии предиката. Комментарии (такие как /* (i) */ и /* (o,i) */) показывают поток(и) параметров для этого предиката.

Занесение фактов во время выполнения программы

    Но время выполнения факты могут быть добавлены во внутреннюю базу данных фактов посредством предикатов: assert, asserta и assertz, или путем загрузки фактов из файла с помощью consult.

    Существует три предиката для добавления одного факта во время выполнения:

   asserta(the fact) % (i)
   asserta(the fact,facts_sectionName) % (i,i)
   assertz(the fact) % (i)
   assertz(the fact,facts_sectionName) % (i,i)
   assert(the fact) % (i)
   assert(the fact,facts_sectionName) % (i,i)

    Предикат asserta вставляет новый факт в базу данных фактов перед имеющимися фактами для данного предиката, a assertz вставляет факты после имеющихся фактов данного предиката. Использование предиката assert дает результат, аналогичный использованию assertz.

    Поскольку имена предикатов базы фактов уникальны внутри программы, для предикатов asserta и assertz всегда известно, в какую базу данных фактов нужно добавлять факт. Однако для того, чтобы обеспечить работу с требуемой базой данных фактов, в целях проверки типа можно использовать необязательный второй аргумент.

    Первый предикат следующего примера вставит факт о Suzanne, описанный предикатом person, после всех фактов person, хранящихся на текущий момент в памяти. Второй - факт о Michael перед всеми имеющимися фактами предиката person. Третий - факт о John после всех других фактов likes в базе данных фактом likesDatabase, а четвертый вставит факт о Shannon в той же базе данных фактов перед всеми другими фактами likes.

   assertz(person("Suzanne","New Haven",35)).
   assertz(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("Shannon","hard work").
   %   ...   другие факты likes...
    likes("John","money").

    Остерегайтесь случайно написать код, утверждающий один и тот же факт дважды. Внутренние базы фактов не предусматривают никакой уникальности, поэтому один и тот же факт может появляться во внутренней базе данных фактов много раз. Однако версию assert с проверкой на уникальность написать очень просто:

   database - people
      person(string,string)
   predicates
      uassert(people)
   clauses
      uassert(person(Name,Address)):-
         person(Name,Address) , 
         !,
         ; % OR
         assert(person(Name,Address)).

Считывание фактов из файла

    Предикат consult считывает из файла fileName факты, описанные в разделе database, и вставляет их в вашу программу в конец соответствующей базы фактов. Предикат consult имеет один или два аргумента:

   consult(fileName) % (i)
   consult(fileName,databaseName) % (i,i)

    Однако в отличие от assertz, если вы вызовите consult только с одним аргументом (без имени базы фактов), то будут считаны лишь факты, которые были описаны в разделе без имени (по умолчанию dbasedom).

    Если вы вызовите consult с двумя аргументами (имя файла и имя базы фактов), то будут проверены только факты из указанной базы фактов. Если файл содержит еще что-нибудь, кроме фактов указанной базы, то предикат consult, когда он дойдет до этой строки, возвратит ошибку.

    Обратите внимание, что предикат consult считывает по одному факту. Если файл содержит десять фактов, а в седьмом факте имеется какая-нибудь синтаксическая ошибка, consult занесет шесть первых фактов в базу данных фактов, после чего выдаст сообщение об ошибке.

    Отметим, что предикат consult может считывать файлы только в том формате, который создает save. Файлы не должны содержать:

    При создании или изменении файла с фактами в редакторе нужно соблюдать аккуратность.

Удаление фактов во время выполнения программы

    Предикат retract унифицирует факты и удаляет их из внутренней базы фактов. Он имеет следующий формат:

   retract(the fact) % (i) 
   retract(the fact,databaseName) % (i,i)

    Предикат retract удаляет первый факт из вашей базы данных, который совпадает с фактом the fact, связывая свободные переменные the fact во время выполнения программы. Удаление фактов из внутренней базы фактов эквивалентно процессу доступа к ним с побочным эффектом удаления унифицировавшихся фактов. retract является недетерминированным, если предикат базы фактов, удаляемый retract, не был объявлен детерминированным. При поиске с возвратом предикат retract удаляет все унифицировавшиеся факты, пока они имеются, после чего он далее не находит нужных фактов и завершается неуспешно.

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

   database
      person(string,string,integer)
   database - likesDatabase
      likes(string,string)
      dislikes(string,string)
   clauses
      person("Fred","Capitola",35).
      person("Fred","Omaha",37).
      person("Michael","Brooklyn",26).

      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 находится только в неименованной базе данных фактов, a 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),nl,
      fail.

    Когда вы в качестве второго аргумента retract задаете имя базы фактов, вы можете не указывать имя предиката базы фактов, из которого вы удаляете факты. В этом случае retract будет искать и удалять все факты в указанной базе данных. Например:

   goal
      retract(X,mydatabase),
      write(X),
      fail.

Удаление нескольких фактов сразу

    Предикат retractall удаляет из вашей базы фактов все факты, сопадающие с образцом the fact. Предикат retractall имеет следующий формат:

   retractall(the fact)
   retractall(the fact,databaseName)
действие retractall аналогично действию, заданному
   retractall(X):-
     retract(X),
     fail, 
     retractall(_).

но значительно быстрее него.

    Очевидно, предикат retractall всегда завершается успешно. Из retractall выходные значения получить нельзя. Это означает, что, как и в случае not, нужно использовать символ подчеркивания для свободных переменных.

    Так же, как и в случае предикатов assert и retract, для проверки типа можно использовать второй аргумент. И, как в случае предиката retract, если при вызове retractall используется символ подчеркивания, то из указанного раздела database можно удалить все факты.

    Следующая цель удаляет все факты о мужчинах из базы фактов с фактами person:

   retractall(person(_,_,_,male)).
    Следующая цель удаляет все факты из базы mydatabase.
   retractall(_,mydatabase).

Сохранение базы фактов во время работы программы

    Предикат save сохраняет факты из указанной базы фактов (раздела database) в файле. Этот предикат имеет один или два аргумента:

   save(fileName) % (i) 
   save(fileName,databaseName) % (i,i)

    При вызове предиката save только с одним аргументом (без имени базы фактов), в файле fileName будут сохранены факты из базы фактов dbasedom, используемой по умолчанию.

    При вызове предиката save с двумя аргументами (имя файла и имя базы фактов), в указанном файле будут сохранены факты из раздела database базы фактов с именем databaseName.

    На следующем шаге мы рассмотрим создание базы данных, располагающейся в оперативной памяти.




Предыдущий шаг Содержание Следующий шаг