Разберите поведение Prolog‑программирования на примере: parent(alice,bob). parent(bob,charlie). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). — объясните механизм резолюции и отката (backtracking), в каких случаях порядок правил и предикатов влияет на вычислимость и производительность, и как избежать бесконечных рекурсий при расширении базы фактов

7 Ноя в 07:01
4 +4
0
Ответы
1
Привожу разбор пошагово и короткие рекомендации.
1) Программа (в KaTeX):
parent(alice,bob). parent(alice,bob). parent(alice,bob).
parent(bob,charlie). parent(bob,charlie). parent(bob,charlie).
ancestor(X,Y):−parent(X,Y). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y):parent(X,Y).
ancestor(X,Y):−parent(X,Z),ancestor(Z,Y). ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). ancestor(X,Y):parent(X,Z),ancestor(Z,Y).
2) Механизм резолюции (SLD‑резолюция) и унификация — по шагам на запросе
?−ancestor(alice,charlie). ?- ancestor(alice,charlie). ?ancestor(alice,charlie).
- Процедура выбирает левую (leftmost) цель: ancestor(alice,charlie)ancestor(alice,charlie)ancestor(alice,charlie).
- Сначала пытается унифицировать с первым правилом (базовый случай)
ancestor(X,Y):−parent(X,Y)ancestor(X,Y) :- parent(X,Y)ancestor(X,Y):parent(X,Y). Унификация даёт подстановку X=alice, Y=charlieX=alice,\;Y=charlieX=alice,Y=charlie.
Нужно доказать цель parent(alice,charlie)parent(alice,charlie)parent(alice,charlie) — она не соответствует фактам, попытка завершается неудачей.
- Происходит откат (backtracking) к альтернативному правилу:
ancestor(X,Y):−parent(X,Z),ancestor(Z,Y)ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y)ancestor(X,Y):parent(X,Z),ancestor(Z,Y). Унификация даёт X=alice, Y=charlieX=alice,\;Y=charlieX=alice,Y=charlie.
Становится набор целей: parent(alice,Z), ancestor(Z,charlie)parent(alice,Z),\;ancestor(Z,charlie)parent(alice,Z),ancestor(Z,charlie).
- Левый цель parent(alice,Z)parent(alice,Z)parent(alice,Z) унифицируется с фактом parent(alice,bob)parent(alice,bob)parent(alice,bob), даёт Z=bobZ=bobZ=bob.
Остаётся цель ancestor(bob,charlie)ancestor(bob,charlie)ancestor(bob,charlie).
- Для ancestor(bob,charlie)ancestor(bob,charlie)ancestor(bob,charlie) сначала проверяется базовый случай parent(bob,charlie)parent(bob,charlie)parent(bob,charlie) — он истинный, значит исходный запрос успешен (подстановка X=alice,Y=charlieX=alice,Y=charlieX=alice,Y=charlie).
- Если есть другие варианты для parent(alice,Z)parent(alice,Z)parent(alice,Z) или другие совпадающие правилa, при возврате Prolog будет откатываться и пробовать их.
3) Откат (backtracking)
- При неудаче Prolog возвращается к последней точке выбора (правило или факт) и пробует следующую альтернативу.
- Откат применяется также для поиска дополнительных ответов после успеха.
4) Когда порядок правил/предикатов влияет
- Prolog делает глубинный поиск (depth‑first) с выбором левого предиката. Поэтому:
- Порядок правил влияет на то, какой путь будет исследован первым (влияет на время до первого ответа и на возможность найти ответ вообще).
- Порядок предикатов в теле влияет на частоту привязки переменных и на возможность раннего отсечения ветвей. Часто выгодно ставить детерминистические/узкие тесты первыми (например, проверку ground/1 или сравнения), чтобы сократить ветвление.
- Важный класс проблем — леворекурсивные определения: если рекурсивный вызов стоит левее немедленного уменьшителя размера, поиск может нырять в бесконечную рекурсию и никогда не дойти до базового случая даже если решение существует.
Пример плохой (леворекурсивной) версии:
ancestor(X,Y):−ancestor(X,Z),parent(Z,Y). ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y). ancestor(X,Y):ancestor(X,Z),parent(Z,Y). Запрос ?−ancestor(alice,charlie). ?- ancestor(alice,charlie). ?ancestor(alice,charlie). будет пытаться сначала доказать ancestor(alice,Z)ancestor(alice,Z)ancestor(alice,Z) рекурсивно снова и снова — возможная бесконечная петля.
5) Как избегать бесконечных рекурсий и улучшить производительность
- Писать базовый (не рекурсивный) случай первым: в примере это уже так.
- Переставлять цели в теле так, чтобы рекурсивный вызов шёл после предикатов, которые сильно ограничивают переменные (пример: сначала найти конкретного ZZZ через parent, затем рекурсивно).
- Использовать накопители / явный список посещённых узлов, чтобы предотвратить циклы:
ancestor(X,Y):−ancestor_vis(X,Y,[]). ancestor(X,Y) :- ancestor\_vis(X,Y,[]). ancestor(X,Y):ancestor_vis(X,Y,[]).
ancestor_vis(X,Y,Visited):−parent(X,Y),¬member(Y,Visited). ancestor\_vis(X,Y,Visited) :- parent(X,Y), \lnot member(Y,Visited). ancestor_vis(X,Y,Visited):parent(X,Y),¬member(Y,Visited).
ancestor_vis(X,Y,Visited):−parent(X,Z),¬member(Z,Visited),ancestor_vis(Z,Y,[Z∣Visited]). ancestor\_vis(X,Y,Visited) :- parent(X,Z), \lnot member(Z,Visited), ancestor\_vis(Z,Y,[Z|Visited]). ancestor_vis(X,Y,Visited):parent(X,Z),¬member(Z,Visited),ancestor_vis(Z,Y,[ZVisited]). (Идея: не возвращаться в уже посещённые вершины.)
- Использовать табуляцию (tabling, memoization) в реализациях Prolog (например, SWI/XSB): директива :−table ancestor/2. :- table\ ancestor/2. :table ancestor/2. обеспечивает SLG‑резолюцию и часто предотвращает зацикливание и повторные вычисления.
- Применять cut (!) и once/1 аккуратно, когда нужно отбросить ненужные альтернативы (влияет на полноту).
- Обеспечить убывающую меру рекурсии (например, длина пути уменьшается) — классический способ для доказуемости завершения.
- Ограничения глубины или итеративное углубление (если нужно гарантировать завершение при большом пространстве).
Короткие практические правила:
- Базовые факты и детерминистические проверки — первыми.
- Рекурсивный вызов — после предикатов, которые конкретизируют аргументы.
- Для графов/циклов — хранить visited или использовать табулирование.
- Профилировать: порядок влияет на скорость; ставьте самые вероятные/узкие правила первыми.
Если нужно, могу показать конкретные варианты кода с visited или пример использования tabling в SWI‑Prolog.
7 Ноя в 07:32
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир