Запросы: как Prolog отвечает
Урок о том, как задавать Prolog вопросы и читать его ответы: true, false, подстановки переменных и перебор решений.
Запрос — это вопрос к базе знаний: Prolog ищет, можно ли подтвердить утверждение, опираясь на известные факты и правила.
В прошлом уроке мы наполнили базу фактами. Но база, к которой нельзя обратиться, бесполезна. Запрос — это способ спросить: «А правда ли, что Том — родитель Боба?» или «Кто вообще дети Тома?». Prolog отвечает, проверяя, согласуется ли вопрос с тем, что записано в базе.
Будем работать с той же семьёй:
parent(tom, bob).
parent(tom, liz).
parent(bob, ann).
parent(bob, pat).
Знак запроса ?-
В интерактивном режиме (его называют toplevel) Prolog показывает приглашение ?- и ждёт вопрос. Самый простой запрос — спросить про конкретный факт:
?- parent(tom, bob).
Вывод:
true.
Prolog просмотрел стопку фактов parent/2, нашёл точное совпадение и ответил true — «да, это правда». А если спросить про то, чего в базе нет?
?- parent(liz, bob).
Вывод:
false.
Такого факта в базе нет, вывести его тоже неоткуда, поэтому ответ — false. Важная оговорка: false в Prolog значит не «это ложь во вселенной», а «я не смог это доказать по своей базе». Это называют предположением о замкнутости мира: чего нет в базе и что нельзя вывести — считается ложным.
Запросы с переменными
Спрашивать «да/нет» — только половина возможностей. Куда интереснее спросить «а кто?». Для этого в запрос подставляют переменную — идентификатор с заглавной буквы. Спросим, кто дети Тома:
?- parent(tom, X).
Вывод:
X = bob.
Prolog нашёл первый факт, который подходит под образец parent(tom, X) — это parent(tom, bob) — и связал переменную X со значением bob. Такую связку «переменная = значение» называют подстановкой (или связыванием, binding). Ответ X = bob читается как: «Утверждение истинно, если X равно bob».
Перебор решений: ; и Enter
Но у Тома два ребёнка! Где же liz? Дело в том, что Prolog показывает по одному решению за раз и ждёт вашей реакции. Если вы хотите следующее решение, нажмите точку с запятой ; (или клавишу пробел/«n» в некоторых системах). Если первого решения достаточно — нажмите Enter, и поиск остановится.
?- parent(tom, X).
X = bob ;
X = liz ;
false.
Разберём по шагам, что произошло после нажатий ;:
| Действие | Ответ Prolog |
| запрос | X = bob — первое решение |
| нажали ; | X = liz — второе решение |
| нажали ; | false — больше нет |
Финальное false означает не ошибку, а «других решений не осталось». Таким образом Prolog способен перебрать все факты, подходящие под образец, — нужно лишь продолжать запрашивать следующее.
Переменная в роли родителя
Переменную можно поставить в любую позицию. Спросим, кто родители Боба и Лиз:
?- parent(Y, bob).
Вывод:
Y = tom.
А можно поставить переменные сразу в обе позиции — тогда Prolog выдаст все пары «родитель — ребёнок» подряд:
?- parent(P, C).
P = tom, C = bob ;
P = tom, C = liz ;
P = bob, C = ann ;
P = bob, C = pat ;
false.
Как работает под капотом
За ответами стоит простой, но мощный механизм. Получив запрос parent(tom, X), Prolog берёт его как образец (цель) и идёт по стопке фактов parent/2 сверху вниз. Для каждого факта он пытается выполнить унификацию — сопоставить образец с фактом так, чтобы они совпали.
Унификация parent(tom, X) с фактом parent(tom, bob) удаётся: tom совпадает с tom, а свободная переменная X связывается с bob. Prolog запоминает это место в стопке (ставит «закладку», которую называют точкой выбора, choice point) и выдаёт первое решение.
Когда вы нажимаете ;, срабатывает откат (backtracking): Prolog возвращается к закладке, развязывает X и продолжает идти вниз по стопке от того же места. Следующий подходящий факт — parent(tom, liz) — даёт X = liz. Когда подходящих фактов больше нет, дальнейший откат возвращает false. Этот цикл «унификация → решение → откат» — сердце выполнения в Prolog.
?- parent(tom, X) │ ├─ факт parent(tom, bob) → X = bob (закладка) │ ↑ нажали ; (откат) ├─ факт parent(tom, liz) → X = liz │ ↑ нажали ; (откат) └─ больше нет → false
Частые ошибки
- Переменная со строчной буквы.
parent(tom, x)— это вопрос «верно ли, что ребёнок Тома именно атомx?», ответfalse. Чтобы спросить «кто?», нужнаXс заглавной. - Ожидание всех ответов сразу. Prolog показывает решения по одному; забыв нажать
;, легко решить, что ребёнок у Тома только один. - Паника от финального false.
falseпосле серии решений — это нормальный сигнал «больше нет», а не сбой. - Забытая точка в запросе. Как и факт, запрос завершается точкой; без неё toplevel будет ждать продолжения.
- Неверная позиция переменной. Перепутав местами родителя и ребёнка (
parent(X, tom)вместоparent(tom, X)), вы спросите не то, что хотели.
Итоги
- Запрос начинается с
?-и заканчивается точкой; он спрашивает базу знаний. - Факт без переменных даёт ответ
true(найден/выведен) илиfalse(не доказан). - Переменная (с заглавной буквы) превращает вопрос «да/нет» в вопрос «кто/что», а ответ — в подстановку
X = .... - Решения выдаются по одному:
;запрашивает следующее, Enter останавливает поиск. - Под капотом работают унификация и откат (backtracking), а итоговое
falseзначит «решений больше нет».