Логическое программирование_УП
.pdf141
inc,
N1 is N-1, ackerman(M,N1,Val1), M1 is M-1, ackerman(M1,Val1,Val).
inc:-
do(X),
X1 is X+1, retract(do(_)), assert(do(X1)).
do(0).
?- ackerman(3,2,X),do(Y). X = 29
Y = 541 Yes
В данном случае предикат ackerman вызвал себя рекурсивно 541 раз. Теперь приведем вариант с запоминающей функцией.
:- dynamic do/1,ackerman/3.
ackerman(0,N,N1):- inc,
N1 is N+1.
ackerman(M,0,Val):- M>0, inc,
M1 is M-1, lemma(ackerman(M1,1,Val)).
ackerman(M,N,Val):- M>0,N>0, inc,
N1 is N-1, lemma(ackerman(M,N1,Val1)), M1 is M-1, lemma(ackerman(M1,Val1,Val)).
142
lemma(P):-
P, asserta((P:-!)).
do(0).
inc:-
do(X),
X1 is X+1, retract(do(_)), assert(do(X1)).
?- ackerman(3,2,X),do(Y). X = 29
Y = 73 Yes
Теперь предикат ackerman вызывает себя только 73 раза. Приведем замечание из [2, стр. 164]. Приведенные примеры
показывали некоторые, безусловно удобные способы применения предикатов assert и retract. Но при использовании этих предикатов необходимо соблюдать особую осторожность. Излишнее и непродуманное применение этих средств нельзя рекомендовать в качестве хорошего стиля программирования. Использование операций подтверждения и извлечения фактов и правил по сути приводит к модификации программы. Поэтому отношения, которые в какой-то момент были действительными, в другое время могут оказаться недействительными. В разное время на одни и те же вопросы система будет давать разные ответы. Поэтому применение значительного количества операций подтверждения и извлечения может затемнить смысл программы. В конечном итоге может оказаться, что поведение программы трудно понять, нелегко объяснить, и поэтому ей нельзя доверять.
143
ЛИТЕРАТУРА
1. Robinson J.A. A machine-oriented logic based on resolution principle. Journal of the ACM, 12: 23–41, 1965.
2.Братко И. Алгоритмы искусственного интеллекта на языке PROLOG: Пер. с англ. — 3-е изд. — М.: Издательский дом
«Вильямс», 2004. — 640 с.
3.В.А. Антимиров, А.А. Воронков, А.И. Дегтярёв, М.В. Захарьящев, В.С. Проценко. Математическая логика в программировании. Обзор // Математическая логика в программировании:
Сб. статей 1980–1988 гг.: Пер. с англ. — М.: Мир, 1991. — 408 с.
4.Грэй П. Логика, алгебра и базы данных: Пер. с англ. — М.: Машиностроение, 1989. — 359 с.
5.Дейкстра Э. Заметки по структурному программированию // У. Дал, Э. Дейкстра, К. Хоор. Структурное программирование. —
М.: Мир, 1975. — С. 7–97.
6.Малпас Дж. Реляционный язык Пролог и его применение. —
М.: Наука, 1990. — 464 с.
7.Одинцов И.О. Профессиональное программирование. Системный подход. — СПб.: БХВ-Петербург, 2002. — 512 с.
8.Стерлинг Л., Шапиро Э. Искусство программирования на языке Пролог. — М.: Мир, 1990. — 235 с.
9.Язык Пролог в пятом поколении ЭВМ: Сб. статей, 1983– 1986 гг. / Сост. Н.И. Ильинский. — М.: Мир, 1988.
144
ПРИЛОЖЕНИЕ
Помощь в SWI-Prolog’е
После инсталляции интерпретатора SWI-Prolog есть возможность обратиться к помощи через ярлык «manual.html». Кроме того, документация для пользователя находится в файле manual.pdf (читается с помощью программы Adobe Acrobat).
Кроме того, SWI-Prolog имеет помощь online. Вызов «?- help(<имя предиката>).» выдает на экран информацию об этом предикате. Вызов «?- help.» выдает информацию о том, как пользоваться этой помощью.
Трассировка выполнения программы
Предикат trace позволяет пользователю отслеживать состояние интерпретатора Пролога.
• trace/0
Стартует трассировщик. В процессе мониторинга в выходной файл выводятся все целевые утверждения, обрабатываемые интерпретатором языка. Зачастую этой информации для пользователя слишком много.
• trace(+Pred)
Эквивалентно вызову trace(+Pred, +all).
• trace(+Pred, + Ports)
Устанавливаются точки трассировки на всех предикатах, удовлетворяющих спецификации Pred. Ports есть список портов
(call, redo, exit, fail). Атом all ссылается на все пор-
ты. Если перед именем порта стоит символ «–», то данный порт не трассируется. Знак «+» говорит о том, что установлена трассировка данного порта.
При трассировке предиката получаем следующую информа-
цию:
1)уровень глубины рекурсивных вызовов;
2)когда предпринимается первая попытка обработки целевого утверждения (порт call);
3)когда цель успешно достигнута (порт exit);
4)возможность других соответствий целевому утверждению (порт redo);
145
5) невозможность достижения цели, поскольку все попытки завершились неудачно (порт fail).
Примеры:
?- trace(hello). % трассировка всех портов предиката hello с любой арностью ?- trace(foo/2, +fail). % трассировка неудач при вызове foo/2
?- trace(bar/1, -all). % прекращение трассировки bar/1.
Проведем трассировку для программы:
родитель( пэм, боб). родитель( том, боб). родитель( том, лиз). родитель( боб, энн). родитель( боб, пэт). родитель( пэт, джим).
предок(X,Y):-
родитель(X,Y).
предок(X,Y):-
родитель(X,Z), предок(Z,Y).
?- trace(родитель).
% |
родитель/2: [call, redo, exit, fail] |
|
Yes |
|
|
[debug] |
?- trace(предок). |
|
% |
предок/2: [call, redo, exit, fail] |
|
Yes |
|
|
[debug] |
?- предок(том,пэт). |
|
T Call: |
(6) |
предок(том, пэт) |
T Call: |
(7) |
родитель(том, пэт) |
T Fail: |
(7) |
родитель(том, пэт) |
T Redo: |
(6) |
предок(том, пэт) |
T Call: |
(7) |
родитель(том, _G414) |
T Exit: |
(7) |
родитель(том, боб) |
T Call: |
(7) |
предок(боб, пэт) |
T Call: |
(8) |
родитель(боб, пэт) |
T Exit: |
(8) |
родитель(боб, пэт) |
T Exit: |
(7) |
предок(боб, пэт) |
T Exit: |
(6) |
предок(том, пэт) |
Yes |
|
|