Page 21 - 4800
P. 21
встановлюється на перший repeat. Другий рядок – це правило, що використовує
самовиклик. Правило (другий repeat) викликає підмету (третій repeat), і цей виклик
здійснюється успішно, тому що факт (перший repeat) відповідає підметі. Отже, правило
також завжди успішне. Предикат repeat буде успішно обчислюватися при кожній новій
спробі його викликати після відкату. Таким чином, repeat – це рекурсивне правило, що
ніколи не буває неуспішним.
Введіть у програму 2.3 опис предикату repeat. Вставте звертання до цього
предиката в якості першої підмети правила query. Запустіть на виконання програму.
Тепер з'явилася можливість повторного вводу, але немає ознаки закінчення і
єдиний варіант виходу з програми – це Ctrl+Break. Але для того, щоб сформувати
ознаку закінчення повторень, необхідно вияснити, як працює програма, і хто ініціює
повтор. Першим виконується предикат repeat, що нічого не робить, далі – стандартні
предикати, що не мають альтернатив, й останнім предикат do_answer, що і забезпечує
відкат після невдачі до предикату repeat завдяки наявності в ньому предиката fail.
Зверніть увагу, що наявність у програмі предикату repeat ще не забезпечує повторів,
оскільки для організації повторів необхідне повернення до предикату repeat.
Але якщо відкат до repeat викликає do_answer, то він повинний і забезпечити, за
певних умов, закінчення цього процесу. Якщо за умову виходу прийняти, наприклад,
введення слова “stop” замість прізвища, то можна довизначити предикат do_answer ще
одним правилом:
do_answer(X) :- X="stop", write("good bye").
яке буде істинним при узгодженні, і яке, через відсутність ознаки невдачі, не викликає
відкату до предиката repeat.
Узагальнюючи викладене, можна зробити висновок про те, що умова виходу з
циклу може визначатися будь-яким предикатом, один з альтернативних описів якого
повинний містити предикат fail або викликати пошук із поверненням, тобто
забезпечувати відкат.
З аналізу навіть найпростіших випадків організації повторюваних процесів, як
реалізованих самою Пролог-системою, так і обумовлених користувачем, можна
зробити висновок про необхідність керування цими процесами з боку користувача.
2.6 Керування пошуком з поверненням
Один з варіантів керування пошуком з поверненням можна ілюструвати на
прикладі предиката do_answer, записаного в дещо іншій формі.
do_answer(X) :-colleague(Z,Y), X==Z, write(" ", X, " -> ", Y), nl, fail.
Альтернативна форма запису не змінює суті даного правила, але дає можливість
показати, що введення нових підцілей у правило можна керувати пошуком з
поверненням. З цього прикладу видно, що друга підмета може виявитися неуспішною
через невідповідність працівника, уніфікованого з першої підмети, із працівником,
уніфікованим через заголовок правила, тобто введеного з клавіатури. Неуспіх
уніфікації другої підмети приведе до того, що відкат виникне до видачі інформації на
екран і предикат fail не буде застосовано. Включення предикату fail у правило,
забезпечує відкат, якщо умови правила будуть виконані і все правило виявиться
успішним.
Ще одним прикладом керування пошуком з поверненням є процедура побудови
меню show_menu.
show_menu:- repeat, write(" 1 - процес 1"), nl, write(" 2 - процес 2"), nl, write(" 0 -
вихід "), nl, nl, write(" Ваш вибір -> "), readint(Menu), Menu < 3, process(Menu),
stop_menu(Menu).
stop_menu(0).
stop_menu(_) :- fail.
У ній repeat використовується так, що після виходу з будь-якого модуля, що
викликається предикатом process(), йде повернення в меню. Виключенням є вибір нуля,
що викликає закінчення програми.
Якщо процедура process() завершиться успіхом, то система робить спробу
виконати процедуру stop_menu(), яка при будь-яких значеннях, крім 0, завершується
невдачею, що викликає відкат до предиката repeat. При значенні вибору рівному 0 –
вона є істинним фактом, і програма завершується.
21