Заметьте, что при добавлении нового правила синтаксис требует, чтобы оно (как аргумент
assert
) было заключено в скобки.
При добавлении нового предложения может возникнуть желание указать, на какое место в базе данных его следует поместить. Такую возможность обеспечивают предикаты
asserta
и
assertz
. Цель
asserta( С)
помещает С в начале базы данных. Цель
assertz( С)
— в
конце. Вот пример, иллюстрирующий работу этих предикатов:
?- assеrt( p( a)), assertz( p( b) ), asserta( p( c) ).
yes
?- p( X).
X = с;
X = а;
X = b
Между
consult
и
assertz
существует связь. Обращение к файлу при помощи
consult
можно в терминах
assertz
определить так: считать все термы (предложения) файла и добавить их в конец базы данных.
Одним из полезных применений предиката
asserta
является накопление уже вычисленных ответов на вопросы. Пусть, например, в программе определен предикат
решить( Задача, Решение)
Мы можем теперь задать вопрос и потребовать, чтобы ответ на него был запомнен, с тем чтобы облегчить получение ответов на будущие вопросы:
?- решить( задача1, решение),
asserta( решить( Задача1, Решение) ).
Если в первой из приведенных целей будет успех, ответ
( Решение)
будет сохранен, а затем использован так же, как и любое другое предложение, при ответе на дальнейшие вопросы.
Преимущество такого "запоминания" состоит в том, что на дальнейшие вопросы, сопоставимые с добавленным фактом, ответ будет получен, как правило, значительно быстрее, чем в первый раз. Ответ будет теперь получен как факт, а не как результат вычислений, требующих, возможно, длительного времени.
Развитие этой идеи состоит в использовании
assert
для порождения всех решений в виде таблицы фактов. Например, создать таблицу произведений всех чисел от 0 до 9 можно так: породить пару чисел X и Y, вычислить Z, равное X * Y, добавить эти три числа в виде строки в таблицу произведений, а затем создать искусственно неуспех. Неуспех вызовет возврат, в результате которого будет найдена новая пара чисел, и в таблицу добавится новая строка и т.д. Эта идея реализована в процедуре
таблица :-
L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
принадлежит( X, L), % Выбрать первый сомножитель
принадлежит( Y, L), % Выбрать второй сомножитель
Z is X*Y,
assert( произв( X,Y,Z) ),
fail.
Вопрос
?- таблица.
потерпит, конечно, неудачу, однако в качестве своего побочного эффекта приведет к добавлению в базу данных целой таблицы произведений. После этого можно, например, спросить, какие пары дают произведения, равные 8:
?- произв( А, В, 8).
А = 1
В = 8;
А = 2
В = 4;
...
Здесь
следует сделать одно замечание, относящееся к стилю программирования. Приведенные примеры показали некоторые явно полезные применения
assert
и
retract
. Однако использование этих отношений требует особой внимательности. Не рекомендуется применять их слишком часто и без должной осторожности - это плохой стиль программирования. Ведь добавляя и удаляя предложения, мы фактически изменяем программу. Поэтому отношения, выполнявшиеся в некоторой ее точке, могут оказаться неверными в другой. В разные моменты времени ответы на одни и те же вопросы будут различными. Таким образом, большое количество обращений к
assert
и
retract
может затемнить смысл программы и станет трудно разобрать, что истинно, а что — нет. В результате поведение программы может стать непонятным, трудно объяснимым, и вряд ли можно будет ей доверять.
Упражнения
7.6.
(а) Напишите вопрос к пролог-системе, который удаляет из базы данных всю таблицу
произв
.
(b) Измените этот вопрос так, чтобы он удалил из таблицы только те строки, в которых произведение равно 0.
7.7. Определите отношение
копия( Терм, Копия)
которое порождает такую копию
Терм
'а
Копия
, в которой все переменные переименованы. Это легко сделать, используя
assert
и
retract
.
7.5. Средства управления
К настоящему моменту мы познакомились с большинством дополнительных средств управления, за исключением
repeat
(повторение). Здесь мы для полноты приводим список всех таких средств.
• отсечение, записывается как '
!
', предотвращает перебор, введено в гл. 5.
•
fail
— цель, которая всегда терпит неудачу.
•
true
— цель, которая всегда успешна.
•
not( P)
— вид отрицания, который всегда ведет себя в точном соответствии со следующим определением:
not( P) :- P, !, fail; true.
Некоторые проблемы, связанные с отсечением и
not
детально обсуждались в гл. 5.
•
саll( P)
активизирует цель
P
. Обращение к
саll
имеет успех, если имеет успех P.
•
repeat
— цель, которая всегда успешна. Ее особое свойство состоит в том, что она недетерминирована, поэтому всякий раз, как до нее доходит перебор, она порождает новую ветвь вычислений. Цель
repeat
ведет себя так, как если бы она была определена следующим образом:
repeat.
repeat :- repeat.
Стандартный способ применения
repeat
показан в процедуре
квадраты
, которая читает последовательность чисел и выдает их квадраты. Последовательность чисел заканчивается атомом
стоп
, который служит для процедуры сигналом окончания работы.