Вы должны попытаться взять большую проблему и разбить ее на несколько меньших проблем. Итак, во-первых: работа с деревьями сложна, списки намного удобнее обрабатывать:
treeToList(Tree, List) :-
% I use an accumulator, starting with an empty list.
treeToList(Tree, [], List).
% The bottom is reached: The accumulated elements are the result.
treeToList(nil, Accu, Accu).
treeToList(bt(E, Left, Right), Accu0, List) :-
Accu1 = [E|Accu0], % prepend the accumulator with the current element
treeToList(Left, Accu1, Accu2), % prepend elements of the left node
treeToList(Right, Accu2, List). % prepend elements of the right node
?- treeToList(bt(6, bt(2, bt(1, nil, nil),
bt(3, nil, nil)),
bt(8, bt(7, nil, nil),
bt(9, nil, nil))), L).
L = [9, 7, 8, 3, 1, 2, 6].
Подсчет в отсортированном списке намного проще, чем подсчет несортированного списка. Prolog имеет для этого встроенный предикат sort / 2 .
?- sort([9, 7, 8, 3, 1, 2, 6], L).
L = [1, 2, 3, 6, 7, 8, 9].
Вы можете подсчитать количество соседних значений и получить оставшиеся разные значения за один шаг:
removeFromFrontAndCount(_, [], [], N, N).
removeFromFrontAndCount(E, [F|Out], [F|Out], N, N) :-
E \= F.
removeFromFrontAndCount(E, [E|In], Out, N0, N2) :-
N1 is N0 + 1,
removeFromFrontAndCount(E, In, Out, N1, N2).
?- removeFromFrontAndCount(1, [1,1,1,2,3,4], L, 0, N).
L = [2, 3, 4],
N = 3 ;
false.
Использование этого помощника - это предикат countAdjacent / 3:
countAdjacent(List0, E, N) :-
[E0|List1] = List0,
removeFromFrontAndCount(E0, List1, List2, 1, CountE),
( % a semicolon is an either-or operator
E = E0, N = CountE;
countAdjacent(List2, E, N)
).
?- countAdjacent([1,1,1,2,2,3], E, N).
E = 1, N = 3 ;
E = 2, N = 2 ;
E = 3, N = 1 ;
false.
И для объединения всего:
numberOfOccurrences(E, Tree, N) :-
treeToList(Tree, List),
sort(List, SortedList),
countAdjacent(SortedList, E, N).
?- numberOfOccurrences(E, bt(6, bt(2, bt(1, nil, nil),
bt(3, nil, nil)),
bt(8, bt(7, nil, nil),
bt(9, nil, nil))), L).
E = 1, L = 1 ;
E = 2, L = 1 ;
E = 3, L = 1 ;
E = 6, L = 1 ;
E = 7, L = 1 ;
E = 8, L = 1 ;
E = 9, L = 1 ;
false.
Весь код :
numberOfOccurrences(E, Tree, N) :-
treeToList(Tree, List),
sort(List, SortedList),
countAdjacent(SortedList, E, N).
treeToList(Tree, List) :-
treeToList(Tree, [], List).
treeToList(nil, Accu, Accu).
treeToList(bt(E, Left, Right), Accu0, List) :-
treeToList(Left, [E|Accu0], Accu1),
treeToList(Right, Accu1, List).
countAdjacent(List0, E, N) :-
[E0|List1] = List0,
removeFromFrontAndCount(E0, List1, List2, 1, CountE),
(
E = E0, N = CountE;
countAdjacent(List2, E, N)
).
removeFromFrontAndCount(_, [], [], N, N).
removeFromFrontAndCount(E, [F|Out], [F|Out], N, N) :-
E \= F.
removeFromFrontAndCount(E, [E|In], Out, N0, N2) :-
N1 is N0 + 1,
removeFromFrontAndCount(E, In, Out, N1, N2).