
- •Введение
- •Принципы логического программирования
- •1.2. Математическая основа языка Пролог
- •1.2. Организация вычислительного процесса
- •1.2.2. Использование переменных
- •1.2.1. Синтаксис фактов и правил
- •1.3. Бэктрекинг
- •2. Основные элементы языка
- •2.1. Имена
- •2.2. Типы данных
- •2.3. Константы и переменные
- •2.4. Программные секции Пролога
- •2.4.1. Секция Domains
- •2.4.2. Секция Predicates
- •2.4.3. Секция Database
- •2.4.4. Секция Clauses
- •2.4.5. Секция Goal
- •3. Язык Пролог в задачах и примерах
- •3.1. Программирование с помощью фактов и правил
- •3.1.2. Первая формулировка задачи поиска в пространстве состояний 51 ад
- •3.1.3. Реализация на Прологе простой вопросно-ответной системы
- •It_is(“отказать в приеме на работу”):- not(this(“есть диплом”)).
- •It_is(“должность научного сотрудника”):- this(“есть диплом”),
- •3.2. Рекурсии
- •Упражнения
- •3.3. Программирование циклических процессов
- •3.4. Работа со списками
- •3.4.1. Описание списков в программе
- •3.4.2. Добавление элемента в список
- •3.4.3. Удаление элемента
- •3.4.4. Принадлежность элемента списку
- •3.4.5. Сцепление (конкатенация) списков
- •3.4.6. Удаление из списка повторяющихся элементов
- •3.4.7. Вычисление суммы элементов списка
- •3.4.8. Обращение списка
- •3.4.9. Нахождение максимального элемента списка
- •3.4.10. Перестановки
- •3.4.11. Примеры использования списков
- •Упражнения
- •3.5. Виды рекурсии
- •3.6. Поиск в пространстве состояний
- •Vshir ( [ [ V | Way ] | _ ], [ V | Way ] ) :- % Голова списка – полученное решение
- •Vshir ( Ways, Resh ). % Продолжение поиска в случае тупикового пути
- •Упражнения
- •3.6. Использование структур
- •3.6.1. Объявление структур
- •База данных с использованием структур.
- •Vife(X) :– family( _ , X , _ ). % X – жена
- •3.6.4. Планирование воздушного путешествия (143 Бр)
- •3.6.5. Реализация Планировщика в терминах структур
- •3.6.6. Задача «Зебра»
- •Упражнения
- •3.7. Динамическая база данных
- •3.7.1. Использование стандартных предикатов динамической базы данных
- •Упражнения
- •3.8. Средства управления
- •3.9. Представление множеств двоичными деревьями
- •3.9. Программы классификации
- •3.9.1. Программа классификации с обратной цепочкой рассуждений
- •Xpositive( X, y ), !. % в базе данных
- •Xnegative( X, y ), !, fail. % Отрицательный ответ обнаружен в базе данных
- •Xpositive("имеет","перья").
- •3.9.2. Программы классификации с прямой цепочкой рассуждений.
- •It_is( X ) :- write( X, “?”), % Механизм диалога
- •3.9. Обработка текстов
- •Verb( string ) % Глагол
- •Упражнения
- •4. Стандартные предикаты
- •4.1. Ввод/вывод
- •4.2. Управление экраном и оконная система
- •4.3. Обработка строк
- •4.4. Преобразование типов
- •4.5. Работа с базой данных
- •4.6. Управляющие предикаты
- •4.7. Прочие стандартные предикаты
- •4.8. Арифметические и логические предикаты
- •Приложение Приложение 1. Примерные варианты лабораторных заданий
- •1. Родословное дерево
- •2. Вопросно-ответная система
- •3. Работа со списками
- •4. Поиск пути на графе.
- •5. Разработайте прототип классификационной экспертной системы
- •6. Построение синтаксического анализатора
- •Рекомендуемая литература
Vshir ( [ [ V | Way ] | _ ], [ V | Way ] ) :- % Голова списка – полученное решение
my_goal ( V ). % Цель достигнута
vshir ( [ [ V | Way ] | Ways ], Resh ) :-
% New_ways - все продолжения пути на один шаг
findall ( X, kandidat ( [ V | Way ], X ), New_ways ),
conc ( Ways, New_ways, Ways1 ), !, % Добавление в конец множества кандидатов
vshir ( Ways1, Resh );
Vshir ( Ways, Resh ). % Продолжение поиска в случае тупикового пути
kandidat ( [ B | Way ], [ B1, B | Way ] ) :-
posle ( B, B1 ),
not ( member ( B1, [ B | Way ] ) ). % Обнаружение зацикливания
member ( X, [ X | _ ] ).
member ( X, [ _ | T ] ) :- member ( X, T ).
conc ( [ ], L, L ).
conc ( [ X | L1 ], L2, [ X | L3 ] ) :-
conc ( L1, L2, L3 ).
goal user_interface ( a, Resh ), write ( Resh ).
Обе эти стратегии (поиск в глубину и поиск в ширину) обносятся к так называемым алгоритмам полного перебора пространства состояний. Они способны получить оптимальное решение, но время получения решения может оказаться неприемлемым. Более изощренные процедуры поиска используют какую-либо дополнительную информацию, отражающую специфику задачи, с тем, чтобы на каждой стадии поиска принимать решение о наиболее перспективных путях поиска. Дополнительная информация, относящаяся к конкретной решаемой задаче, используемая для управления поиском, называется эвристикой, а алгоритмы, использующие эвристику – эвристическими. Эвристические алгоритмы могут быть реализованы как на базе поиска в глубину, так и в ширину.
Рассмотрим детальную реализацию поиска пути на графе на базе алгоритма поиска в глубину. После несложного расширения эта программа сможет отвечать на следующие вопросы:
- как добраться из одной вершины в другую, не посещая дважды одной и той же вершины;
- как пройти по маршруту, посетив заданную вершину;
- как добраться до нужной вершины, избежав некоторых вершин.
Рис. Граф, представляющий фрагмент пространства состояний
Пусть имеется следующий граф, представляющий фрагмент пространства состояний.
Центральной частью программы будет база данных (факты), содержащая данные о сети дуг между вершинами. Эта информация представлена в виде двухаргументного отношения road, например:
road( v1, v3 ).
road( v4, v1 ).
road( v3, v5 ).
Необходимо иметь информацию об обратных направлениях. Вместо того, чтобы переписывать факты, воспользуемся правилом road1:
road1( T1, T2 ) :– road( T1, T2 );
road( T2, T1 ).
Для решения этой задачи удобнее всего собирать в список все пройденные вершины. Будем исходить из следующих соображений: если существует дуга из вершины X в вершину Y, то путь найден (базовое правило), иначе чтобы найти путь из X в Y, надо найти дугу из X в Z и затем путь из Z в Y (рекурсивное правило):
way(X,Y,[ ]):–road1(X,Y).
way(X,Y,[Z|W]):–road1(X,Z), way(Z,Y,W).
Очевиден недостаток такой записи: система, не смущаясь, будет находить маршруты, многократно проходящие через одну и ту же вершину. Чтобы избежать этого зацикливания, следует добавить проверку непринадлежности очередной вершины найденному списку вершин:
way(X,Y,[Z|W]]):- road1(X,Z), way(Z,Y,W), not(member(Z,W)).
Здесь member – знакомый предикат проверки принадлежности элемента списку.
Рекурсия неизбежно становится нехвостовой, поскольку для предиката member необходимо иметь конкретизированную величину списка W (а список этот конкретизируется только при обратном прохождении рекурсии). Но в такой рекурсии опасность зацикливания не устранена: можно без конца совершать переходы из одной вершины в другую и обратно, т.к. до проверки не принадлежности вершины маршруту вычисления просто не доходят. Очевидна необходимость хвостовой рекурсии. Можно перейти к такой рекурсии, если начать вычисления от конкретизированного списка (например, пустого) и иметь уже тем больший список, чем больше вложенность рекурсии, т.е. накапливать список в правой части правила. Но в таком случае накопленный список надо зафиксировать в базовом правиле введением дополнительного аргумента:
way(X,Y,W,W):–road1(X,Y).
way(X,Y,W,L):– road1(X,Z), not(member(Z,W)),
way(Z,Y,[Z|W],L).
Для запуска этой программы можно воспользоваться интерфейсным предикатом go, аргументы которого содержат начальный и конечный пункты:
go(Here,There):– way(Here,There,[Here],W),
add(There,W,W1), reverse(W1,W2), write(W2).
Здесь исходный список маршрута конкретизируется начальной вершиной. К списку найденного маршрута добавляется конечный пункт (предикат add). Предикат reverse выполняет инверсию этого списка. Клозы этих последних предикатов было записано ранее.
Для нахождения пути из одного заданного города в другой следует при запуске цели конкретизировать переменные Here и There. Если требуется найти путь для двух конкретных городов, проще всего ввести значения переменных стандартным предикатом Readln. В некоторых задачах необходимо организовать перебор в пространстве вершин. Наиболее корректное решение в этом случае – получить список всех вершин, а затем последовательно конкретизировать переменные вершинами из этого списка, например, с помощью предиката member или away. Вершины можно собрать в список с помощью стандартного предиката findall, использовав базу данных (фактов) задачи (см. раздел «Стандартные предикаты»).