Программирование в среде Visual Prolog. Учебное пособие
.pdf
Рис. 5.5
Так, для дерева, показанного на рис. 5.5, мы получим следующий вывод:
Исходный код такого решения:
domains
treetype = tree(integer, treetype, treetype); empty predicates
print_tree(treetype, integer) print_spaces(integer) clauses
print_tree(empty, _Depth):-!. print_tree(tree(TopValue, Left, Right), Depth):- SubtreesDepth = Depth + 1,
print_tree(Left, SubtreesDepth), print_spaces(Depth), write(TopValue), write(«<«), nl, print_tree(Right, SubtreesDepth). print_spaces(SpaceNumber):-
SpaceNumber <= 0, !; write(«\t»),
TailSpaceNumber = SpaceNumber - 1, print_spaces(TailSpaceNumber).
21
Функция print_spaces выводит заданное количество пробелов для «перемещения значения» на заданный уровень иерархии. Функция вывода дерева отличается от рассмотренной ранее тем, что в процессе вывода рассчитывается текущая глубина узла.
5.4. ГРАФЫ
Графы используются во многих приложениях, например, для представления отношений, ситуаций или структур задач. Граф определяется как множество вершин вместе с множеством ребер. Если ребра направлены, то их называют дугами. Дуги задаются упорядоченными парами. Такие графы называются направленными. Ребрам можно приписывать веса, имена или метки произвольного вида, в зависимости от конкретного приложения.
На рис. 5.6 изображены два графа: a – граф, б – направленный граф. Каждой дуге приписан ее вес.
Рис. 5.6
Для представления графов в языке Пролог можно использовать три способа.
1.Использование факта языка Пролог для описания дуг или ребер графа.
2.Использование списка из структур данных: структура задает ребро графа.
3.Использование двух списков: списка вершин и списка ребер. Две самые распространенные операции, которые выполняются над
графами:
поиск пути между двумя вершинами;
поиск в графе подграфа, который обладает некоторыми заданными свойствами.
22
Пример 5.1
Определить наличие связи между вершинами графа, показанного на рис. 5.7.
Рис. 5.7
Две вершины графа связаны, если существует последовательность ребер, ведущая из первой вершины во вторую. Используем для описания структуры графа факты языка Пролог.
domains top=symbol predicates edge (top, top)
/* аргументы обозначают имена вершин */ path( top,top)
/*Предикат connected(symbol, symbol) определяет отношение связанности вершин.*/
clauses edge (a, b). edge (c, d). edge (a, c). edge (d, e). edge (b, d). edge (f, g). path (X, X).
path (X, Y):- edge (X, Z), path (Z, Y).
Пример 5.2
Решить задачу, используя списочные структуры для представления графа. Граф задается двумя списками: списком вершин и списком ребер. Граф задается списком структур: структура описывает ребро графа.
23
domains
edge= e(symbol, symbol) list=edge*
predicates
path(list, symbol, symbol) member(list,edge)
/*предикат path(graf, symbol, symbol) определяет отношение связности вершин.*/
clauses member([H|_],H).
member([_|T],X):-member(T,X). %path ([],_,_):-fail. path(L,X,Y):-member(L,e(X,Y)),!.
path(L,X,Y):-member(L,e(X,Z)),path(L,Z,Y). goal
path ([e(a, b),e(b, c),e(a, f),e(c, d),e(f,d)], a, d).
%path ([e(a, b),e(c, d),e(a, c),e(d, e),e(b, d),e(f, g)], b, g).
Поиск пути на графе
Программы поиска пути на графе относятся к классу так называемых недетерминированных программ с неизвестным выбором альтернативы, т. е. втакихпрограммахнеизвестно, какоеизнесколькихрекурсивных правил будет выполнено для доказательства до того момента, когда вычисление будет успешно завершено. По сути дела, такие программы представляют собой спецификацию алгоритма поиска в глубину для решения определенной задачи.
Пример 5.3
Определитьпутьмеждувершинамиграфа, представленногонарис. 5.8: A – переменная, обозначающая начало пути;
B – вершина, в которую нужно попасть;
P – ациклический путь на графе (ациклический путь – это путь, не имеющий повторяющихся вершин).
domains top=symbol listtop=top* predicates
24
edge (top, top)
/* аргументы обозначают имена вершин */ path (top, top, listtop)
/*Предикат path( top, top, listtop) создает список из вершин, составляющих путь.*/
clauses edge (a, b). edge (b, c). edge (c, a). edge (b, d). edge (d, e).
path (A, A, [A]).
path (A, B, [A\P]):-edge(A, N), path(N, B, P).
Рис. 5.8
С помощью поиска в глубину осуществляется корректный обход любого конечного дерева или ориентированного ациклического графа. Однако встречаются задачи, где требуется производить обход графа с циклами. В процессе вычислений может образоваться бесконечный программный цикл по одному из циклов графа.
Неприятностей с зацикливанием можно избежать двумя способами: ограничениемнаглубинупоискаилинакоплениемпройденныхвершин. Последний способ можно реализовать при помощи модификации отношения path. К аргументам отношения добавляется дополнительный аргумент, используемыйдлянакопленияужепройденных приобходевершин, для исключения повторного использования одного и того же состояния применяется проверка.
25
Пример 5.4
Написать программу обхода конечного ориентированного графа, представленного на рис. 5.9.
Рис. 5.9
domains top=symbol listtop=top* predicates edge(top,top)
path (top,top,listtop,listtop) path (top,top) member(top,listtop) reverse(listtop,listtop,listtop) clauses
edge(a,b). edge(b,c). edge(c,a). edge(b,d). edge(d,e). member(A,[A|_]):-!.
member(A,[_|T]):-member(A,T). reverse([],T2,T2). reverse([H|T],T1,T2):-reverse(T,[H|T1],T2).
26
path(A,B,P,[B|P]):-edge(A,B). path(A,B,P,P2):-edge(A,N),not(member(N,P)), P1=[N|P], path (N,B,P1,P2). path(A,B):-path(A,B,[A],P),reverse(P,[],Res),write(Res). goal
path(a,e).
Практическое задание
1. Исследовать работу одной из логических программ, задающих отношения над объектами рекурсивных типов, по указанию преподавателя (см. задания для исследования):
а) записать предложенную программу на Турбо-Прологе; б) выполнить программу в пошаговом режиме и построить дерево
вывода при ее работе; в) модифицировать программу, вставляя в предложения программы
предикат отсечения (!); г) выполнить модифицированную программу в пошаговом режиме
и построить дерево вывода; д) сравнить деревья вывода в том и другом случае и определить
«цвет» отсечения.
2.Разработать и отладить программу решения индивидуального задания.
3.Продемонстрировать преподавателю работу составленных программ.
4.Оформить отчет по выполненной работе.
5.Защитить работу.
Задания для исследования
1.Сравнение деревьев с точностью до изоморфизма [9, программа
3.25, с. 56].
2.Подстановка терма в дерево [9, программа 3.26, с. 56].
3.Обход бинарного дерева [9, программа 3.27, с. 57]:
сверху вниз;
слева направо;
снизу вверх.
27
4.Распознавание многочленов [9, программа 3.28, с. 58].
5.Правила дифференцирования [9, программа 3.29, с. 58].
6.Выполнимость булевых формул [9, программа 3.31, с. 61].
7. Поиск элемента в двоичном справочнике [2, программа 9.7,
с. 283].
Индивидуальные задания
1. Напишите программу, которая преобразует логическую формулу
F1 в логическую формулу F2, используя следующие аксиомы: true V F=true и false & F=false, где F – логическая формула.
2.Напишите программу, которая определяет, является ли данный графсвободнымдеревом. Свободноедерево– это связныйграф, неимеющий циклов.
3.Напишитепрограмму, определяющую, внормальнойлиформезадана арифметическая сумма, т. е. имеет ли она вид A + B, где A – константа, а В – сумма в нормальной форме.
4.Напишите программу, которая преобразует логическую формулу F1 в логическую формулу F2 внесением всех операторов отрицания внутрь конъюнкций и дизъюнкций.
5.Определите отношение tree_insert(X, Tree, Tree1), выполненное,
если упорядоченное дерево Tree1 получается вставкой элемента X в упорядоченное дерево Tree. Если X уже присутствует в Tree, то Tree1 совпадает с Tree.
6.Напишите программу, которая по заданному направленному графу определяет самый длинный цикл.
7.Напишите программу, которая по данной логической формуле определяет, будетилинетэтаформулатавтологией, т. е. формулой, верной для всех комбинаций истинностных значений ее аргументов. Например,
((X& Y) V ¬X)V ¬ (Y &Z) – тавтология.
8. Определите отношение ex_tree(Tree, Ex), выполненное, если число Ex является значением выражения, представленного в виде двоичного дерева. Допустимыми операциями в арифметическом выражении являются: +, –, /, *.
28
9.Определитеотношениеsum_tree(Tree_of_integer, Sum), выполнен-
ное, если число Sum равно сумме целых чисел, являющихся вершинами дерева Tree_of_integer.
10.Определите отношение ordered(tree), выполненное, если дерево Tree являетсяупорядоченнымдеревомцелыхчисел, т. е. число, стоящее
влюбой вершине дерева, больше любого элемента в левом поддереве и меньше любого элемента в правом поддереве.
11.Определите процедуру, вычисляющую глубину двоичного дерева. Глубина пустого дерева равна 0, одноэлементного – 1.
12.Определите отношение max_element(Tree, Element), выполненное, еслиElement являетсямаксимальнымсредиэлементов, хранящихся
вдереве.
13.Составьте программу subtree(S, T), определяющую, является ли S поддеревом Т.
14.Напишите программу, которая по заданному конечному автомату и слову в некотором алфавите определяет, принадлежит ли это слово языку, допускаемому таким автоматом.
29
Глава 6
ВНЕЛОГИЧЕСКИЕ ПРЕДИКАТЫ ПРОЛОГА
СТАНДАРТНЫЕ ПРЕДИКАТЫ РАБОТЫ СО СТРОКАМИ
ВПрологеимеетсярядпредикатов, находящихсязарамкамимодели логического программирования: внелогические средства управления процессом вычислений. Они называются внелогическими предикатами. Такие предикаты в процессе решении логических целей порождают побочные эффекты. Внелогические предикаты были добавлены в Пролог для того, чтобы сделать поиск решений более эффективным.
Существует три основных вида внелогических предикатов:
предикаты, относящиеся к вводу-выводу,
предикаты, обеспечивающие доступ к программе и ее обработку,
предикаты для связи с внешней операционной системой.
К внелогическим предикатам можно также отнести предикаты отсечения, fail и отрицания.
6.1. ПРЕДИКАТЫ ВВОДА-ВЫВОДА В ПРОЛОГЕ
Очень важный класс предикатов, вызывающих побочные эффекты, образуют предикаты, относящиеся к вводу-выводу. Любой практическийязыкпрограммированиядолженобеспечиватьсредствавводаивывода. Однако вычислительная модель Пролога препятствует введению операций ввода-вывода в виде чистых компонентов языка.
Основной предикат ввода – read (X). При поиске решения для такой целисчитываетсятермизтекущеговходногопотока: обычноэтоданные, вводимые с клавиатуры. Введенный терм унифицируется с X, при этом
30
