Развитие этой идеи состоит в использовании 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
показан в процедуре квадраты
, которая читает последовательность чисел и выдает их квадраты. Последовательность чисел заканчивается атомом стоп
, который служит для процедуры сигналом окончания работы.
квадраты :-
repeat,
read( X),
( X = стоп, !;
Y is X*X, write( Y), fail ).
7.6.
При помощи механизма автоматического перебора можно получить одни за другим все объекты, удовлетворяющие некоторой цели. Всякий раз, как порождается новое решение, предыдущее пропадает и становится с этого момента недоступным. Однако у нас может возникнуть желание получить доступ ко всем порожденным объектам сразу, например собрав их в список. Встроенные предикаты bagof
(набор) и setof
(множество) обеспечивают такую возможность; вместо них иногда используют предикат findall
(найти все).
Цель
bagof( X, P, L)
порождает список L всех объектов X, удовлетворяющих цели P. Обычно bagof
имеет смысл применять только тогда, когда X и P содержат общие переменные. Например, допустим, что мы включили в программу следующую группу предложений для разбиения букв (из некоторого множества) на два класса — гласные и согласные:
класс( а, глас).
класс( b, согл).
класс( с, согл).
класс( d, согл).
класс( e, глас).
класс( f, согл).