Чтение онлайн

ЖАНРЫ

Программирование на языке Пролог для искусственного интеллекта

Братко Иван

Шрифт:

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
показан в процедуре
квадраты
, которая читает последовательность чисел и выдает их квадраты. Последовательность чисел заканчивается атомом
стоп
, который служит для процедуры сигналом окончания работы.

квадраты :-

 repeat,

 read( X),

 ( X = стоп, !;

Y is X*X, write( Y), fail ).

7.6. bagof, setof и findall

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

bagof
(набор) и
setof
(множество) обеспечивают такую возможность; вместо них иногда используют предикат
findall
(найти все).

Цель

bagof( X, P, L)

порождает список L всех объектов X, удовлетворяющих цели P. Обычно

bagof
имеет смысл применять только тогда, когда X и P содержат общие переменные. Например, допустим, что мы включили в программу следующую группу предложений для разбиения букв (из некоторого множества) на два класса — гласные и согласные:

класс( а, глас).

класс( b, согл).

класс( с, согл).

класс( d, согл).

Поделиться с друзьями: