- •Введение
- •1. Составление программ
- •1.1. Основные определения
- •1.2. Декларации в программах
- •1.3. Объявление предикатов и типов их аргументов
- •1.4. Другие разделы программы
- •Упражнения
- •2. Механизмы доказательства правил
- •2.1. Сопоставление с откатом
- •2.2. Рекурсия
- •Упражнения
- •3.Операции в Visual Prolog. Ввод-вывод
- •3.1. Операции
- •3.2. Предикаты ввода-вывода
- •4. Управление процессом доказательства правил
- •4.1. Искусственный откат
- •4.2. Отсечение
- •4.3. Повтор, определяемый пользователем
- •5. Списки
- •5.1. Процедуры обработки списков
- •5.2. Организация стеков и очередей
- •6. Внутренняя база фактов
- •7. Иерархическая организация данных
- •8. Работа с деревьями и графами
- •8.1. Двоичные деревья
- •8.2. Графы
- •9. Работа с именами и строками
- •Заключение
- •Библиографический список
- •Оглавление
8.2. Графы
Граф определяется как множество вершин вместе с множеством ребер, каждое из которых задается парой вершин. К ребрам можно приписывать стоимости, имена или метки произвольного вида в зависимости от конкретного приложения. Существует несколько способов представления графов средствами Пролога.
1. Представление дуги графа в виде факта.
дуга(a, b).
дуга (a, c).
дуга (b, d).
дуга (b, c).
Если дуга имеет вес, то он может быть отображен в виде третьего аргумента предиката.
2. В виде списков вершин и ребер
a) граф([a, b, c, d, e]), [дуга(a, b), дуга(b, c), дуга(b, d) . . .])
б) [верш(a, [b, d]), верш(b, [c, d]), верш(c, [e]) . . .]
Две самые распространённые задачи, которые выполняются с графами:
поиск пути между двумя вершинами;
поиск в графе подграфа, который обладает некоторыми заданными свойствами.
Все процедуры, к которым сводятся эти задачи, являются
рекурсивными.
Рассмотрим два примера решения первой задачи
1. Определение длины пути между двумя вершинами неориентированного размеченного графа, приведенного на рис. 5. В программе граф задан с помощью предиката «дуга/3», третьим аргументом которого является длина дуги. Для того, чтобы отразить возможность перехода из одной вершины в другую как в одну, так и в другую сторону, введен предикат «соседняя». В рекурсивном правиле «пройти» задается исходная и конечная вершины и переменная, которая в результате вычислений конкретизируется длиной всего маршрута. При движении по графу маршрут прокладывается сначала от первой вершины до вершины X, а затем из X до второй вершины.

Рис.5
DOMAINS
вершина = symbol
путь = integer
PREDICATES
nondeterm дуга(вершина, вершина,путь)
nondeterm соседняя(вершина, вершина, путь)
nondeterm пройти(вершина, вершина, путь)
CLAUSES
дуга(a1, a5, 5).
дуга(a1, a4, 7).
дуга(a2, a4, 2).
дуга(a3, a4, 4).
соседняя(X, Y, D):-
дуга(X, Y, D); дуга(Y, X, D).
пройти (Старт, Цель, Путь):-
соседняя(Старт, Цель,Путь).
пройти(Старт, Цель, Путь):-
соседняя(Старт, X, Путь1),
Путь=Путь1+Путь2,!.
GOAL
пройти(a2, a5, X), write(X).
X=14
(Отсечение после выражения Путь для традиционного Пролога обязательно.)
2. Определим маршрут между 2 вершинами, но постараемся получить его в виде списка пройденных вершин. Граф приведен на рис.6. Усложним задачу введением обязательной для посещения вершину а8 и двух вершин, в которые нельзя заходить – а5 и а7.

Рис. 6
Важная особенность программы – пройденные вершины заносятся в список. Это происходит благодаря предикату «путь», который определяется рекурсивно. В третий аргумент предиката добавляется пройденная вершина в том случае, если она незапрещенная и ранее не посещалась.
DOMAINS
вершина = symbol
список_вершин = вершина*
PREDICATES
nondeterm дуга(вершина,вершина)
nondeterm соседняя(вершина,вершина)
nondeterm избегать(список_вершин)
nondeterm пройти(вершина,вершина)
nondeterm путь(вершина,вершина,список_вершин)
nondeterm member(вершина,список_вершин) /*прове
ряет принадлежность списку*/
CLAUSES
дуга(a1,a7).
дуга(a1,a4).
дуга(a2,a4).
дуга(a3,a4).
дуга(a3,a9).
дуга(a5,a4).
дуга(a5,a8).
дуга(a6,a4).
дуга(a6,a8).
дуга(a7,a8).
дуга(a8,a9).
member(X,[X|_]).
member(X,[_|H]):-member (X,H).
соседняя(X,Y):-дуга(X,Y); дуга(Y,X).
избегать([a5,a7]).
пройти(Старт,Цель):-путь(Старт,Цель,[Старт]).
путь(Вершина, Вершина, Маршрут):-
member(a8, Маршрут), %посетили обязательную
write(Маршрут).
путь(Вершина, Цель, Маршрут):-
соседняя(Вершина, СледВершина),
избегать (Опасные),
/* не заходим в опасные*/
not(member(СледВершина, Опасные)),
/* не заходим в пройденные*/
not(member(СледВершина, Маршрут)),
путь(СледВершина, Цель,
[СледВершина | Маршрут]).
/*пройденная заносится в голову списка как в стеке*/
GOAL
пройти(a1, a9).
Маршрут=[a9, a8, a6, a4, a1 ]
Как обычно, VIP предложит только один вариант маршрута; для поиска всех решений следует применить искусственный откат. Пройденные вершины занесены в Маршрут в обратном порядке, поэтому перед выводом следует использовать процедуру инвертирования списка.
Примеры решения второй задачи работы с графами подробно рассмотрены в [1].
