Программы на языке Prolog выполняются не так, как в других языках программирования. Как уже говорилось в предыдущих уроках, задача Prolog программы доказать какое–либо утверждение и для этого используются логические операции, которые были рассмотрены в предыдущем уроке. В этом уроке мы рассмотрим, как Prolog выполняет программы и использует алгоритм поиска с возвратом.
В предыдущем уроке мы изучили предикаты конъюнкции и дизъюнкции. Подумайте, что выведет следующий код:
write('Hello, '), write('Hello, ') ; write('Hexlet!'), write('World!').
Hello, Hello, true ; Hexlet!World! true.
Как говорилось в предыдущем уроке, конъюнкция – логическое "И", а дизъюнкция – логическое "ИЛИ".
Благодаря конъюнкции первые и последние два оператора были выполнены вместе, а полученные результаты были выведены раздельно. Код выше можно прочитать так:
Выведи строку 'Hello, ' И строку 'Hello, ' ИЛИ выведи строку 'Hexlet!' И выведи строку 'World!'. Предикат write
возвращает "ИСТИНУ" (true). Таким образом Prolog нашел два подходящих решения.
А что выведет следующий код?
write(p), (write(q) ; write(r)), write(t).
pqt true ; rt true.
Почему вывод получился именно таким и почему буква 't' вывелась два раза, если в коде только один вывод этой буквы? Все дело в том, как Prolog интерпретирует программу. Фактически программа будет находить все возможные решения, которые в конечном итоге подтвердят утверждение. Для выполнения этой задачи Prolog использует поиск с возвратом (backtracking). Программа перебирает все возможные решения, возвращаясь в разные точки, в которых могло произойти разветвление решения на разные варианты.
Давайте разберем пример выше:
write(p)
и переходит к следующему предикатуwrite(q) ; write(r)
. Они соединены логическим "ИЛИ", которое разбивает решение на 2 разных. Фактически это означает, что программа может выполнить один предикат "ИЛИ" другойwrite(t)
и выполняет его. Программа должна бы завершиться, но мы помним, что у нас был предикат "ИЛИ", который разделил решение на 2 разных;
, но выполняет уже следующий предикат write(r)
, а за ним и оставшийся предикат write(t)
Совет: вывести все решения сразу можно, дописав в конце
, nl, fail.
. Получится такой код:
write(p), (write(q) ; write(r)), write(t), nl, fail.
Расставьте предикаты ,
и ;
так, чтобы программа вывела:
a true ; bc true.
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1:- style_check(-singleton).
2:- dynamic error_happened/1.
3
4:- assertz(error_happened('OK')).
5
6user:message_hook(Term, error, _Lines) :-
7 retract(error_happened('OK')),
8 assertz(error_happened(error)),
9 fail.
10:- include(main).
11:- forall(call(writer), (nl, write('true ;'), nl)).
12
13:- begin_tests(backtracking).
14
15test(040, Output == 'a\ntrue ;\nbc\ntrue ;\n') :-
16 with_output_to(atom(Output), forall(call(writer), (nl, write('true ;'), nl))),
17 error_happened(X).
18
19:- end_tests(backtracking).
Решение учителя откроется через: