KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Иван Братко - Программирование на языке Пролог для искусственного интеллекта

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

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Иван Братко, "Программирование на языке Пролог для искусственного интеллекта" бесплатно, без регистрации.
Перейти на страницу:

?- 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, согл).

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

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

Тогда мы можем получить список всех согласных, упомянутых в этих предложениях, при помощи цели:

?- bagof( Буква, класс( Буква, согл), Буквы).

Буквы = [d, c, d, f]

Если же мы в указанной цели оставим класс букв неопределенным, то, используя автоматический перебор, получим два списка букв, каждый из которых соответствует одному из классов:

?- bagof( Буква, класс( Буква, Класс), Буквы).


Класс = глас

Буквы = [а,e]


Класс = согл

Буквы = [b, c, d, f]

Если bagof( X, P, L) не находит ни одного решения для P, то цель bagof просто терпит неуспех. Если один и тот же X найден многократно, то все его экземпляры будут занесены в L, что приведет к появлению в L повторяющихся элементов.

Предикат setof работает аналогично предикату bagof. Цель

setof( X, P, L)

как и раньше, порождает список L объектов X, удовлетворяющих P. Только на этот раз список L будет упорядочен, а из всех повторяющихся элементов, если таковые есть, в него попадет только один. Упорядочение происходит по алфавиту или по отношению '<', если элементы списка — числа. Если элементы списка — структуры, то они упорядочиваются по своим главным функторам. Если же главные функторы совпадают, то решение о порядке таких термов принимается по их первым несовпадающим функторам, расположенным выше и левее других (по дереву). На вид объектов, собираемых в список, ограничения нет. Поэтому можно, например, составить список пар вида

Класс / Буква

при этом гласные будут расположены в списке первыми ("глас" по алфавиту раньше "согл"):

?- setof( Класс/Буква, класс( Буква, Класс), Спис).

Спис = [глас/а, глас/e, согл/b, согл/с, согл/d, согл/f]

Еще одним предикатом этого семейства, аналогичным bagof, является findall.

findall( X, P, L)

тоже порождает список объектов, удовлетворяющих P. Он отличается от bagof тем, что собирает в список все объекты X, не обращая внимание на (возможно) отличающиеся для них конкретизации тех переменных из P, которых нет в X. Это различие видно из следующего примера:

?- findall( Буква, класс( Буква, Класс), Буквы).

Буквы = [a, b, c, d, e, f]

Если не существует ни одного объекта X, удовлетворяющего P, то findall все равно имеет успех и выдает L = [].

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*