Скачиваний:
40
Добавлен:
22.05.2015
Размер:
144.38 Кб
Скачать

Лабораторная работа 2 Поиск с откатом

Настоящая лабораторная работа выполняется в консольном приложении и является базовой в изучении работы машины логического вывода Пролога, основанного на механизмах унификации (unification) и поиска с возвратом (backtracking).

Цвета автомобилей

Пусть имеется множество марок автомобилей {КАМАЗ, Toyota, BMW} и множество возможных цветов кузова автомобиля {зелёный, красный}. Представим эти множества в Прологе и осуществим поиск по различным критериям. Опишем множество автомобилей в виде предиката с именем автомобиль/1 с одним аргументом, принадлежащим домену string. Для этого создадим новый консольный проект, скомпилируем его и вставим в файл main.pro перед разделом clauses новый раздел с именем class facts, и объявим в нём недетерминированный предикат:

class facts

автомобиль : (string).

Множество автомобилей зададим в разделе clauses:

clauses

автомобиль("КАМАЗ").

автомобиль("Toyota").

автомобиль("BMW"). 

Аналогичным способом добавим в файл main.pro объявление и определение предиката цвет/1. После указанных модификаций файл main.pro должен иметь такой исходный код:

implement main

open core, console

constants

className = "com/visual-prolog/main".

classVersion = "$JustDate: $$Revision: $".

c

Объявление предикатов

lass facts

автомобиль : (string).

цвет : (string).

clauses

classInfo(className, classVersion).

а

Предложения предиката

автомобиль/1

втомобиль("КАМАЗ").

автомобиль("Toyota").

автомобиль("BMW").

ц

Предложения предиката

цвет/1

вет("зелёный").

цвет("красный").

run():-

Тело цели – предиката run()

init(),

succeed().

end implement main

goal

mainExe::run(main::run).

Следует заметить, что предложения одного предиката необходимо группировать, то есть располагать друг за другом и не смешивать с предложениями другого предиката.

Пример 1. Осуществим поиск автомобиля "Toyota". Для этого удалим из цели предикат succeed(), который Visual Prolog автоматически вставляет при создании проекта, и модифицируем цель следующим образом:

run():-

init(),

Первое предложение

автомобиль("Toyota"), !,

write("Есть такой автомобиль"),

_=readchar();

Второе предложение

write("Нет такого автомобиля"),

_=readchar().

Запустите программу и исследуйте её поведение, указывая различные марки автомобиля, даже отсутствующие в программе. Всплывающее окно предупреждений о неиспользуемом предикате можно игнорировать.

Как Пролог осуществляет поиск на дереве решений и запоминает пройденные на нём пути? Рассмотрим по шагам работу механизма поиска с возвратом, используя классическую стековую модель. При вызове первого предложения предиката run/0 Пролог запоминает в стеке адрес второго предложения (адрес возврата). Адрес возврата нужен для того, чтобы в случае неудачи при выполнении первого предложения можно было бы быстро перейти к выполнению второго предложения, вытолкнув его адрес из стека.

В первом предложении вызывается недетерминированный предикат автомобиль("Toyota"). При его выполнении в стек заносится адрес возврата для предиката автомобиль/1, в результате чего в стеке находятся уже два адреса возврата. Далее возможны два исхода:

  • Если хотя бы одно из предложений предиката автомобиль/1 содержит аргумент "Toyota", то вызов этого предиката будет успешен и Пролог выполнит отсечение. Отсечение удалит из стека все адреса возврата, помещённые туда при выполнении предиката run/0 (а их всего два), тем самым лишая Пролог возможности перейти к выполнению остальных предложений предиката автомобиль/1 и run/0 в случае отката. После этого выполнятся две процедуры – вывод на экран сообщения "Есть такой автомобиль" и ожидание ввода с клавиатуры.

  • Если ни одно из предложений предиката автомобиль/1 не содержит аргумент "Toyota", то вызов этого предиката будет неуспешен и произойдёт откат назад. Для этого из стека автоматически выталкивается адрес возврата и Пролог переходит к выполнению второго предложения – выводит на экран сообщение "Нет такого автомобиля" и ожидает ввод с клавиатуры. При этом в стек ничего не помещается, так как третьего предложения в предикате run/0 нет.

Именно так и работает механизм поиска с откатом. Подведём итог:

    • если предикат имеет больше одного предложения, то при его выполнении в стеке запоминается адрес возврата к очередному предложению;

    • если происходит откат назад, то Пролог переходит к адресу, который выталкивается из стека;

    • отсечение удаляет из стека все адреса возврата, помещённые туда в ходе выполнения предиката, в теле которого стоит это отсечение, а также всех его подцелей.

Пример 2. Для нахождения всех автомобилей целевой предикат должен быть таким:

run():-

init(),

автомобиль(X),

write("Найден автомобиль: ",X), nl,

fail;

write("Конец поиска"),

_=readchar().

Запустите программу и исследуйте её поведение.

Обратите внимание, что для получения всех решений мы убрали отсечение и вместо него поставили предикат fail, который принудительно вызывает откат назад.

Как работает механизм поиска с откатом в этом случае? При вызове предиката автомобиль(X) Пролог помещает в стек адрес возврата и находит первое решение X="КАМАЗ", после чего выводит сообщение на экран и выполняет предикат fail. Этот предикат вызывает откат назад и из стека выталкивается адрес возврата. Выполняется второе предложение предиката автомобиль/1. При этом в стек заталкивается новый адрес возврата и находится новое решение X="Toyota", выводится сообщение на экран и происходит принудительный откат назад. Опять из стека выталкивается адрес возврата и выполняется последнее третье предложение в результате чего X="BMW". Однако в отличие от предыдущих предложений в стек ничего не помещается и принудительный откат назад выталкивает из стека адрес второго предложения предиката run/0. На экран выводится сообщение "Конец поиска" и выполняется ожидание ввода с клавиатуры.

Пример 3. Для нахождения всех комбинаций автомобиль‑цвет целевой предикат должен быть таким:

run():-

init(),

автомобиль(Авто), цвет(Цвет),

write(Авто," - ",Цвет), nl,

fail;

write("Конец перебора"),

_=readchar().

end implement main

goal

mainExe::run(main::run).

Запустите программу и исследуйте её поведение. Запомните порядок вывода решений:

КАМАЗ - зелёный

КАМАЗ - красный

Toyota - зелёный

Toyota - красный

BMW - зелёный

BMW - красный

Конец перебора

Обратите внимание, что вызов двух недетерминированных предикатов

автомобиль(Авто), цвет(Цвет), fail

с откатом осуществляет перебор всех цветов для каждого автомобиля. Это объясняется тем, что с помощью отката Пролог будет перебирать все цвета до тех пор, пока они не закончатся. И только после этого предикат цвет(Цвет) завершится неудачей, что вызовет откат назад и Пролог найдёт новый автомобиль для которого перебор всех цветов повторится заново. Получается своеобразный цикл в цикле.

Задание 1. Найдите все комбинации цвет‑автомобиль.

Задание 2. Найдите все комбинации автомобиль‑автомобиль и объясните полученный результат.

Соседние файлы в папке Лабораторные работы