
- •Предисловие
- •Глава 1 Введение в Пролог
- •Глава 2 Синтаксис и унификация
- •2.1. Синтаксис термы
- •Константы
- •Переменные
- •Область действия переменных
- •Сложные термы, или структуры
- •Синтаксис операторов
- •Синтаксис списков
- •Синтаксис строк
- •Утверждения
- •Запросы
- •Ввод программ
- •2.2 Унификация
- •Глава 3 Арифметические выражения
- •3.1. Введение
- •3.2. Арифметические выражения
- •3.3. Арифметические операторы
- •3.4. Вычисление арифметических выражений
- •3.5. Сравнение результатов арифметических выражений
- •Глава 4 Рекурсия
- •4.1. Стратегия «разделяй и властвуй»
- •Пример 4.1.1
- •Фаза разбиения
- •Фаза решения задачи
- •4.2. Восходящая стратегия
- •4.3. Рекурсия и эффективность
- •Пример 4.3.2
- •Глава 5 Структуры данных
- •5.1. Списки списковая форма записи
- •А на запрос
- •Некоторые стандартные целевые утверждения для обработки списков
- •5.2. Бинарные деревья представление бинарных деревьев
- •Бинарное дерево на рис.5.2.1 имеет левое поддерево
- •Представление множеств с помощью бинарных деревьев
- •Будем называть линейным представление такого вида, как на рис.5.2.3, и сбалансированным - такое, как на рис.5.2.2.
- •Левому поддереву соответствует отсортированный список
- •Глава 6 Операторы
- •6.1. Операторы и структуры синтаксис операторов
- •Свойства операторов
- •6.2. Позиция операторов
- •6.3. Приоритет операторов
- •6.4. Ассоциативность операторов
- •6.5. Спецификаторы
- •6.6. Операторы объявления
- •6.7. Пример: вычисление многочленов
- •6.8. Системные операторы
- •Глава 7 Механизм возврата и процедурная семантика
- •7.1. Механизм возврата
- •7.2. Пример: задача поиска пути в лабиринте
- •Целевое утверждение не удается согласовать с первым утверждением
- •Если мы введем
- •Если затем мы введем
- •7.3. Обработка фактов с помощью механизма возврата
- •7.4. Предикат repeat
- •7.5. Декларативная и процедурная семантики
- •Характеристики процедуры
- •7.6. Вопросы эффективности
- •Глава 8 Отсечение
- •8.1. Почему используют отсечение?
- •8.2. Использование отсечения
- •Выдав запрос
- •Обратившись к системе с запросом
- •8.3. Ловушки отсечения
- •Глава 9 Встроенные предикаты
- •9.1. Встроенные предикаты
- •9.2. Обновление базы данных Пролога
- •Добавление и удаление утверждений
- •Считывание утверждений в базу данных
- •Предикаты сохранения и восстановления
- •9.3. Особенности ввода и вывода чтение символов
- •Запись символов
- •Считывание термов Предикаты
- •Запись термов
- •9.4. Обработка файлов
- •Редактирование программ на прологе
- •Распечатка предикатов
- •Глава 10 Модули. Пример разработки системы
- •10.1. Модули
- •Пример 10.1.1
- •10.2. Пример разработки системы: деревья решений
- •Описание дерева решений
- •Разработка системы
- •Целевое утверждение
- •Глава 11 Отладка
- •11.1. Трассировка
- •Включение и выключение механизма трассировки
- •Необязательные параметры трассировки
- •Предикаты трассировки
- •Режимы трассировки
- •11.2. Установка контрольных точек
- •Возможные действия в контрольной точке
- •Включение и выключение режима контрольных точек
- •Необязательные параметры режима контрольных точек
- •Предикаты для работы с контрольными точками
- •Режимы, связанные с контрольными точками
- •Основные отладочные предикаты
- •Статистическая информация
7.2. Пример: задача поиска пути в лабиринте
В качестве примера использования механизма возврата напишем процедуру для поиска пути в лабиринте. Лабиринт представлен фактами вида:
стена(I,J) для позиции в 1-м ряду и J-й колонке, где есть стена
отсутств_стена(I,J) для позиции в 1-м ряду и J-й колонке, где нет стены
выход(I,J) для позиции в 1-м ряду и J-й колонке, являющейся выходом
Рассмотрим небольшой лабиринт:
*** *** *** *** ***
*** *** *** ***
*** *** ***
*** *** *** ***
Каждая позиция в нем - это ячейка из 1х3 символов. Последний ряд лабиринта описывается фактами:
стена(4,1).
стена(4,3).
стена(4,4).
отсутств_стена(4,2).
Если задана исходная позиция, путь к выходу можно найти следующим образом.
Граничное условие:
Если исходная позиция является выходом, то путь найден.
Рекурсивные условия:
Ищем путь из исходной позиции в северном направлении. Если пути нет, идем на юг. Если пути нет, идем на запад. Если нельзя, идем на восток. Если соседняя позиция на севере (юге, западе, востоке) является стеной, то нет смысла искать путь из начальной позиции к выходу. Чтобы не ходить кругами, будем вести список позиций, в которых мы побывали.
Изложенному способу решения задачи соответствует процедура путь: она ищет путь (второй аргумент) к выходу из некоторой позиции (первый аргумент). Третьим аргументом является список позиций, где мы побывали.
/* Терм a(I,J) представляет позицию в
/* 1-м ряду и J-й колонке.
/* Нашли путь ?
путь(a(I,J),[a(I,J)],Были) :- выход(I,J).
/* Пытаемся идти на север
путь(a(I,J),[a(I,J) | P], Были) :-
K is I-1,
можем_идти(a(K,J), Были),
путь(a(K,J),P,[a(K,J) | Были]).
/* Пытаемся идти на юг
путь(a(I,J),[a(I,J) | P],Были) :-
К is I+1,
можем_идти (a(K,J) ,Были),
путь(a(K,J),P,[a(K,J) | Были]).
/* Пытаемся идти на запад
путь(a(I,J),[a(I,J) | P],Были) :-
L is J-1,
можем_идти(а(I,L),Были),
путь(а(I,L),Р,[а(I,L) | Были]).
/* Пытаемся идти на восток
путь(а(I,J),[а(I,J) | Р],Были) :-
L is J+1,
можем_идти(а(I,L),Были),
путь(а(I,L),Р,[а(I,L) | Были]).
/* в позицию a(I,J) можно попасть при
/* условии, что там нет стены и мы
/* не побывали в ней прежде
можем_идти(а(I,J),Были) :-
отсутств_стена(I,J),
not(принадлежит(а(I,J),Были)).
Для того чтобы понять, каким образом процедура ищет путь к выходу, рассмотрим процесс согласования запроса с описанием лабиринта, изображенного на рис.7.1.1 ,а:
?- путь(а(4,2),Р,[а(4,2)]).
Выходом из лабиринта является позиция выход(3,1).
Выбор первого утверждения не приводит к согласованию целевого утверждения, поскольку а(4,2) - не выход. Во втором утверждении делается попытка найти путь в северном направлении (рис.7.1.1,б), т.е. согласовать целевое утверждение
путь(а(3,2),Р2,[а(3,2),а(4,2)]).
Целевое утверждение не удается согласовать с первым утверждением
путь(а(3,2),Р2,[а(3,2),а(4,2)])
так как а(3,2) не является выходом. Во втором утверждении предпринимается попытка найти путь, двигаясь на север (рис. 7.1.1,в), т.е. согласовать целевое утверждение
путь(а(2,2),Р3,[а(2,2),а(3,2),а䰔4,2)]).
Ни одно из утверждений не может согласовать
путь(а(2,2),Р3,[а(2,2),а(3,2),а(4,2)]).
Первое утверждение - потому, что а(2,2) не является выходом, второе - потому, что северная позиция является стеной, третье утверждение - потому, что в южной позиции мы уже побывали, а четвертое и пятое утверждения - потому, что западная и восточная границы - это стены.
Неудача в согласовании
путь(а(2,2),Р3,[а(2,2),а(3,2),а(4,2)])
заставляет Пролог-систему вернуться в ту точку, где было выбрано второе утверждение при попытке согласовать
путь(а(3,2),Р2,[а(3,2),а(4,2)]).
Решение пересматривается и выбирается третье утверждение.
В третьем утверждении осуществляется попытка найти путь, двигаясь на юг, но она оказывается неудачной, поскольку мы уже побывали в позиции а(4,2). Тогда, чтобы согласовать
путь(а(3,2),Р2,[а(3,2),а(4,2)]),
выбирается четвертое утверждение. Мы успешно находим путь, двигаясь в западном направлении к позиции а(3,1), которая и является выходом (рис.7.1.1,г). Рекурсия сворачивается, и в результате получается путь
Р=[а(4,2),а(3,2),а(3,1)]
другие решения (да/нет)? да
Других решений нет
Альтернативный путь
[а(4,2),а(3,2),а(2,2),а(3,2),а(3,1)]
мы получить не можем, потому что не разрешается дважды бывать в одной и той же позиции.
Описанная процедура не обязательно находит кратчайший путь к выходу. Например, для лабиринта, представленного на рис.7.1.2,а, найден путь, показанный на рис.7.1.2,б. Кратчайший путь можно найти, генерируя альтернативные пути с помощью вызова состояния неудачи и запоминая кратчайший из них.
************
*** ******
******
*** + ******
а) путь(а(4,2),Р,[а(4,2)])
Идем на север
(утверждение 2)
************ Идем на север ************
*** ****** (утверждение 2) *** + ******
+ ****** ******
*** ****** *** ******
б) пyть(а(3,2),Р2, в) путь(а(2,2),РЗ,
[а(3,2),а(4,2)]) [а(2,2),а(3,2),а(4,2)]
неудача
Возвращаемся к путь(а(3,2),Р2,[а(3,2),а(4,2)]).
Не можем идти на юг, так как уже были в той позиции (утверждение 3)
Идем на запад ************
(утверждение 4) *** ******
+ ******
*** ******
г) целевое утверждение путь(а(3,1),Р4,[а(3,1),а(3,2),а(4,2)]) согласуется с утверждением 1
Рис.7.1.1. Согласование целевого утверждения путь(а(3,1),Р,[а(4,2)])
Конечно, будут построены только пути без повторения позиций, но для поиска кратчайшего пути это не имеет значения: если найден путь с повторным посещением позиции, то, очевидно, существует и более короткий, но без повторного посещения.
Прежде чем мы напишем процедуру для поиска кратчайшего пути, введем встроенные предикаты, которые помогут запомнить кратчайший путь, найденный к настоящему моменту.
Встроенный предикат asserta(X) добавляет Х в базу данных, причем перед утверждениями, имеющими тот же главный функтор, что и X, а предикат assertz(X) - после таких утверждений. Устранить из базы данных утверждение, сопоставимое с X, можно с помощью встроенного предиката retract(X).
Рассмотрим, например, базу данных:
запись (первая).
запись (вторая).