Деревья
9.2. Произвольное дерево
В произвольном корневом дереве каждая вершина может иметь любое количество поддеревьев (рис. 9.1 (b)).
Полиморфный домен таких деревьев так же, как и в предыдущем случае, определяется рекурсивно:
domains
tree{Elem} = t(Elem, tree{Elem}*).
Бинарная структура хранит элемент дерева и список его поддеревьев.
В следующей программе рекурсивно строится дерево потомков человека. Дерево строит предикат createTree/1. Определение этого предиката содержит всего одно правило. Список поддеревьев получается как результат применения предиката высшего порядка list::map/2 к списку "детей" корня дерева. Он строит для каждой вершины поддерево с корнем в этой вершине.
Дерево выводится на печать (предикат print/1), при этом корень дерева располагается слева, а поддеревья справа. Вершины одного уровня находятся на одинаковом расстоянии от левой границы окна консоли. В определении этого предиката участвует предикат второго порядка list::forAll/2.
Построить дерево и вывести его на печать нетрудно и без применения предикатов высшего порядка. Для этого следует ввести дополнительные предикаты, которые обрабатывают списки деревьев:
class predicates
createTree: (string Name) -> tree{string}.
createTreeList: (string Name*) -> tree{string}*.
clauses
createTree(X) = t(X, createTreeList([Y || parent(X, Y)])).
createTreeList([X | L]) = [createTree(X) | createTreeList(L)].
createTreeList([]) = [].
class predicates
print: (tree{Elem}).
print: (tree{Elem}, charCount).
printTreeList: (tree{Elem}*, charCount).
clauses
print(Tree):-
print(Tree, 0).
print(t(X, TreeList), N):-
write(string::create(N, "\t"), X), nl,
printTreeList(TreeList, N + 1).
printTreeList([T | L], N):-
print(T, N),
printTreeList(L, N).
printTreeList([], _).
В программе также строится список вершин дерева. Предикат get_nd/1 недетерминированно возвращает произвольную вершину дерева.
class facts - rel
parent: (string Родитель, string Ребенок).
male: (string).
female: (string).
domains
tree{Elem} = t(Elem, tree{Elem}*).
class predicates % построение дерева потомков
createTree: (string Name) -> tree{string}.
clauses
createTree(X) =
t(X, list::map([Y || parent(X, Y)], {(Z) = createTree(Z)})).
class predicates % печать дерева
print: (tree{Elem}).
print: (tree{Elem}, charCount).
clauses
print(Tree):-
print(Tree, 0).
print(t(X, TreeList), N):-
write(string::create(N, "\t"), X), nl,
list::forAll(TreeList, {(T):- print(T, N + 1)}).
class predicates % обход дерева
get_nd: (tree{Elem}) -> Elem multi.
clauses
get_nd(t(A, _)) = A.
get_nd(t(_, TreeList)) = get_nd(list::getMember_nd(TreeList)).
run():-
file::consult("fam.txt", rel),
Tree = createTree("Иван"),
print(Tree), nl,
write([Elem || Elem = get_nd(Tree)]),
_ = readLine().
Пример
9.4.
Дерево потомков
Ниже реализуются операции над произвольными деревьями. Находится высота дерева, заданное поколение вершин дерева, сумма четных элементов дерева и количество вершин дерева. Выполняется замена элементов дерева другими элементами.
open core, console, list
domains
tree{Elem} = t(Elem, tree{Elem}*).
class facts
arc: (unsigned, unsigned).
clauses
arc(1, 2). arc(1, 3). arc(1, 4). arc(2, 5). arc(2, 6).
arc(3, 7). arc(7, 8). arc(7, 9). arc(7, 10). arc(9, 11).
class predicates
createTree: (unsigned) -> tree{unsigned}.
print: (tree{Elem}).
print: (tree{Elem}, charCount).
clauses
createTree(X) =
t(X, map([Y || arc(X, Y)], {(Z) = createTree(Z)})).
print(Tree):-
print(Tree, 0).
print(t(X, TreeList), N):-
write(string::create(N, "\t"), X), nl,
forAll(TreeList, {(T):- print(T, N + 1)}).
class predicates % вершины заданного уровня
get_nd: (tree{Elem}, positive) -> Elem nondeterm.
clauses
get_nd(t(A, _), 0) = A:- !.
get_nd(t(_, TrList), N) = get_nd(getMember_nd(TrList), N - 1).
class predicates % высота дерева
height: (tree{Elem}) -> integer.
height_nd: (tree{Elem}, integer) -> integer nondeterm.
clauses
height(Tree) = maximum([N || N = height_nd(Tree, 0)]).
height_nd(t(_, []), N) = N.
height_nd(t(_, TreeList), N) =
height_nd(getMember_nd(TreeList), N + 1).
class predicates % подсчеты
sum: (tree{unsigned}) -> unsigned.
count: (tree{Elem}) -> positive.
clauses
% сумма четных вершин
sum(t(A, TrList)) = C + fold(TrList, {(T, S) = sum(T) + S}, 0):-
C = if A mod 2 = 0 then A else 0 end if.
% количество всех вершин
count(t(_, TrList)) = 1 + fold(TrList, {(T, S) = count(T) + S}, 0).
class predicates % замена заданных вершин
replace: (tree{unsigned}, unsigned, unsigned) -> tree{unsigned}.
clauses
replace(t(V, TreeList), A, B) =
t(C, map(TreeList, {(T) = replace(T, A, B)})):-
C = if V = A then B else V end if.
run():-
Tree = createTree(1),
print(Tree),
write("\n\nПоколение 3: ", [Elem || Elem = get_nd(Tree, 3)]),
write("\nВысота дерева: ", height(Tree)),
write("\nСумма четных элементов дерева: ", sum(Tree)),
write("\nКоличество вершин дерева: ", count(Tree)),
write("\nЗамена 9 на 100:\n\n"),
Tree1 = replace(Tree, 9, 100),
print(Tree1),
_ = readLine().
Пример
9.5.
Обработка произвольных деревьев
Упражнение 2. Определите предикаты sum/1, count/1 и replace/3, которые предназначены для вычисления суммы четных элементов дерева, количества всех вершин дерева и замены одного элемента другим, не используя предикаты высшего порядка fold и map класса list. Введите дополнительные предикаты, которые обрабатывают списки деревьев.
В языке Visual Prolog имеются реализации красно-черных деревьев (класс redBlackTree), цифровых деревьев (класс radixTree) и др.
Упражнения
- Замените элементы двоичного дерева заданного уровня на заданный элемент.
- Замените элементы произвольного дерева заданного уровня на заданный элемент.
- Найдите количество всех вершин двоичного дерева.
- Вычислите сумму четных вершин произвольного дерева.
- Найдите количество терминальных вершин двоичного дерева.
- Составьте список терминальных вершин произвольного дерева.
- Вставьте заданное поддерево к заданной вершине произвольного дерева.
- Удалите поддеревья у всех вершин произвольного дерева, равных заданному элементу.
- Найдите путь от одной вершины к другой в дереве, вершины которого хранят попарно различные элементы.
- Вычислите сумму целых чисел, хранящихся в вершинах красного цвета в красно-черном дереве.
- Определите "черную" высоту красно-черного дерева.
- Выведите на печать красно-черное дерево (см. класс redBlackTree) так, чтобы вершины красного цвета печатались красным цветом.