Рекурсивные вычисления
Использование рекурсии обеспечивает организацию циклических вычислений в Прологе. Рекурсия применяется обычно в ситуациях, когда либо число возможных решений заранее не известно, либо когда обрабатываются структуры данных с произвольным числом элементов.
Рекурсивное описание правила содержит в своем теле ссылку на заголовок этого же правила. При этом возможны следующие варианты рекурсивных правил:
1) правая рекурсия
pr1():-pr11(), pr12(), ..., pr1()
2) левая рекурсия
pr1():-pr1(),pr21(), pr22(),...
3) обобщенная рекурсия
pr1():-pr11(),pr12(),..., pr1(), pr21(), pr22() .....
Для того чтобы во время выполнения рекурсивного правила не происходило зацикливания, необходимо предусмотреть условия завершения рекурсии. Их можно реализовать двумя способами:
1) заданием в программе альтернативного правила или факта рr1(), не содержащего рекурсии (выход произойдет при успешном выполнении этого альтернативного правила);
2) формированием условия выхода одним из предикатов рr11 (), рr12()... - выход происходит, если в процессе выполнения правила хотя бы один из предикатов завершается неуспехом.
Предикаты рr21(), рr22()...не влияют на выполнение рекурсии - Они выполняются только после выхода из нее и получают значения переменных из стека, в который они помещаются во время выполнения рекурсии. Производимые при этом вычисления называют хвостовыми вычислениями.
Основные идеи реализации рекурсивных определений в Прологе рассмотрим на примере вычисления факториала. Программа на Прологе имеет вид:
domains
number, product=integer
predicates
fact(number,product)
clauses
317
fact(N,R):-Next_N=N-1,
fact(Next_N,P),
R=N*P.
goal
fact(3,Res),write(" Факториал 3= " , Res),nl.
Действия при выполнении программы по шагам представлены в табл. 24.1.
Таблица 24.1
Действия при выполнении программы
Вызов предиката |
Подстановки |
Вычисления |
Хвостовые вычисления |
Результаты вычислений |
fact(3,Res) |
N=3, Res=R, |
Next_N=N-1=2 |
R=N*P |
Res=R=3*2=6 |
fact(2,P) |
N'=2, P=R' |
Next_N'=N'-1=2 |
R'=N'P' |
P=R'=2*1=3 |
fact(1,F) |
1=1, P'=1 |
|
|
|
<
/p>
На первом шаге выполняется целевой запрос fact(3,Res) и выбирается первый вариант решения: правило fact( 1,1):-!, сопоставление для него заканчивается неуспехом, так как 3#1, происходит откат и выбирается второе правило. Для него сопоставление с заголовком правила заканчивается успешно, при этом выполняется подстановка N=3, Res=R, тело правила помещается в стек и начинают отрабатываться предикаты тела. Первый из них - сравнение Next_N=N-1, оно завершается успешно, переменная Next_N становится равной 2.
Затем инициируется вызов fact(2,P), снова выбирается первый вариант правила, сопоставление 1 #2 приводит к неуспеху, происходит откат и выбор второго варианта правила. Здесь сопоставление с заголовком правила заканчивается успешно, при этом выполняется подстановка N'=2, P'=R', новая копия правила помещается в стек и начинается его выполнение. Первое из них - сравнение Next_N'=N'-1 - завершается успешно, копия переменной Next_N' становится равной 2-1 =1.
Затем выполняется вызов fact(1,P'). Снова производится сопоставление с первым вариантом, и вот здесь оно становится успешным, подстановка имеет вид 1=1, Р'=1, тело первого правила пусто, поэтому никаких действий не инициирует и начинается выполнение "хвостов", оставшихся в стеке от копий второго правила, в результате этих вычислений переменная Res получит значение, равное 6, которое и будет выведено на экран следующим предикатом запроса write("Факториал 3= ", Res). Отметим, что в первом утверждении применено отсечение !. Сделано это для того, чтобы в случае использования внешнего запроса вида fасt(1 ,Res), т.е. запроса на вычисление факториала 1, после успешного сопоставления запроса с первым утверждением (1 = 1,Res=1) отсечь второй вариант правила для нахождения альтернативного решения. Если этого не сделать, то второе правило приведет к зацикливанию и ошибке. Для обеспечения корректной работы программы для запроса fact(0, Res) в раздел
clauses нужно добавить еще одно утверждение, а именно fact(0,1):-!.
318
317 :: 318 :: Содержание
Содержание раздела