- •ПрОграммирование в лоГике
- •1. Факты и правила
- •2. Запросы (цели)
- •3. Переменные
- •4. Комментарии (слайд 7)
- •5. Основные разделы программ (слайд 8)
- •6. Стандартные домены
- •7. Сопоставление и унификация
- •8. Поиск с возвратом
- •8.1 Основные правила поиска с возвратом
- •8.2 Управление поиском решений
- •8.2.1 Предикат fail
- •8.2.2 Отсечение (cut)
8.2 Управление поиском решений
Встроенный механизм поиска с возвратом в Прологе может привести к поиску ненужных решений, в результате чего теряется эффективность (в том случае, когда желательно найти только одно решение). В то же время, в ряде случаев может оказаться необходимым продолжить поиск дополнительных решений, даже если целевое утверждение уже согласовано. В Прологе имеются два инструментальных средства, которые дают возможность управлять механизмом поиска с возвратом: включение поиска с возвратом при его отсутствии, и отключение поиска с возвратом при его наличии.
8.2.1 Предикат fail
Предикат fail используется для инициализации поиска с возвратом.
Рассмотрим, как работает предикат fail. Действует fail очень просто – цель с использованием данного предиката НИКОГДА не доказывается, а, следовательно, всегда включается поиск с возвратом. Для получения такого же эффекта можно сформулировать, например, вот такую цель: 3=2. Эффект будет абсолютно тем же самым. Предикат fail используется в тех случаях, когда в программе есть внутренняя цель, и необходимо позаботиться о нахождении всех возможных решений.
Пример 8. (слайд 13)
Распечатать названия всех континентов, относительно которых есть данные в разделе предложений.
PREDICATES
continent(string)
CLAUSES
continent("Евразия").
continent("Северная Америка").
continent("Южная Америка").
continent("Африка").
continent("Антарктида").
continent("Австралия").
print:-
continent(Continent_name),
write(Continent_name,"\n"), fail.
print.
GOAL
print.
Если в теле правила print отсутствует предикат fail, то тестирование программы выдаст только первое подходящее решение. Поставим в конце правила предикат fail. В результате тестирования будут выданы все возможные решения.
Предикат fail всегда неуспешен, т.е. он не может быть согласован. Поэтому, как только после согласования предыдущих предикатов Пролог обращается к предикату fail, осуществляется поиск с возвратом. При этом Пролог возвращается в ту точку программы, после которой возможны множественные решения. Такое обращение называется недетерминированным. В отличие от недетерминированного, детерминированное обращение может произвести только одно решение.
Помещать подцель после fail в теле правила бесполезно, т.к. такая подцель никогда не будет достигнута.
8.2.2 Отсечение (cut)
Отсечение cut (обозначается !) используется для запрета возможности возврата.
Через отсечение невозможно совершить откат (поиск с возвратом). Отсечение помещается в программу таким же образом, как и подцель в теле правила. Когда процесс проходит через отсечение, немедленно удовлетворяется обращение к cut и выполняется обращение к очередной подцели (если таковая имеется). Однажды пройдя через отсечение, уже невозможно произвести откат к подцелям, расположенным в обрабатываемом предложении перед отсечением, и также невозможно возвратиться к другим предложениям, определяющим обрабатываемый предикат (предикат, содержащий отсечение).
Отсечение применяется в одном из двух основных случаев:
а) если заранее известно, что определенные посылки никогда не приведут к осмысленным решениям – это зеленое отсечение;
б) если отсечения требует сама логика программы для исключения из рассмотрения альтернативных подцелей – это красное отсечение.
Пример 9. (слайды 14-15)
Составить программу, в которой обеспечивается генерация случайным образом трех натуральных чисел, а затем выясняется, может ли существовать треугольник, для которого эти числа являются длинами его сторон. Если треугольник существует, то программа определяет его вид относительно сторон и углов.
PREDICATES
vid(integer,integer,integer)
tip(integer,integer,integer)
suchestv(integer,integer,integer)
CLAUSES
suchestv(A,B,C):-
(A+B-C)*(A+C-B)*(B+C-A)>0,
write("треугольник существует"), nl,
vid(A,B,C), tip(A,B,C). //1
suchestv(A,B,C):-
(A+B-C)*(A+C-B)*(B+C-A)<=0,
write("треугольник не существует"),nl.
vid(A,B,C):-
(A*A+B*B-C*C)*(A*A+C*C-B*B)*(B*B+C*C-A*A)>0,
write("остроугольный"),nl.
vid(A,B,C):-
(A*A+B*B-C*C)*(A*A+C*C-B*B)*(B*B+C*C-A*A)<0,
write("тупоугольный"),nl.
vid(A,B,C):-
(A*A+B*B-C*C)*(A*A+C*C-B*B)*(B*B+C*C-A*A)=0,
write("прямоугольный"),nl.
tip(A,B,C):-
A=C, B=C,
write("равносторонний"), nl.
tip(A,B,C):-
(A-B)*(B-C)*(C-A)=0,
write("равнобедренный"), nl.
tip(A,B,C):-
A<>B,B<>C,C<>A,
write("обычного вида"), nl.
GOAL
random(10,A),write(A),nl,
random(10,B),write(B),nl,
random(10,C),write(C),nl,
A*B*C<>0,
suchestv(A,B,C),
readchar(_).
Программа при помощи встроенных предикатов random и write конкретизирует значения переменных A, B, C, выводит их на экран и приступает к согласованию предиката существования треугольника. В процессе согласования терма 1 программа будет обращаться к правилам для определения вида и типа треугольников.
Если бы мы первоначально записали правила раздела CLAUSES несколько иначе, а именно (слайд 16)
CLAUSES
suchestv(A,B,C):- //1
(A+B-C)*(A+C-B)*(B+C-A)>0,ъ
write("треугольник существует"), nl,
vid(A,B,C), tip(A,B,C),!.
suchestv(_,_,_):- //2
write("треугольник не существует"),nl.
vid(A,B,C):-
(A*A+B*B-C*C)*(A*A+C*C-B*B)*(B*B+C*C-A*A)>0,
write("остроугольный"),nl,!.
vid(A,B,C):-
(A*A+B*B-C*C)*(A*A+C*C-B*B)*(B*B+C*C-A*A)<0,
write("тупоугольный"),nl,!.
vid(_, _, _):- write("прямоугольный"),nl. //3
tip(A,B,C):-
A=C, B=C, write("равносторонний"), !, nl.
tip(A,B,C):-
(A-B)*(B-C)*(C-A)=0, write("равнобедренный"),!, nl.
tip(_, _, _):- //4
write("общего вида"), nl.
Отсечение в термах имеет характер красного. В терме 2 мы убрали условие несуществования треугольника, имея в виду, что обратное ему условие описано в терме 1, а последующее отсечение предусматривает запрет на обращение к предложениям, определяющим обрабатываемый предикат. Вследствие этого нам необходимо обратить внимание на то, что теперь тело правила в терме 2 не связано с переменными, а значит возможно следующее описание:
suchestv(_,_,_):- //2
write("треугольник не существует"),nl.
Похожие действия (убрали условие в термах) относительно предикатов vid(A,B,C) и tip(A,B,C) позволяют охарактеризовать отсечения как красные, а описание термов 3 и 4 дать через анонимные переменные.
Если в данной редакции запустить программу на выполнение, то она будет работать точно так же, как и в первом варианте. Более того, в первом варианте программы можно добавить зеленое отсечение в те же термы. Это никак не повлияет на работу программы, но сократит количество выполняемых операций.
