Проблема в том, что такие предикаты, как tree_elements/2
и avl_tree_planter/2
, необратимы. Я предполагаю, что вы заметили проблему после вызова avl_tree_planter/2
с первым необоснованным аргументом. Например, следующие запросы не завершаются:
?- tree_elements(Tree,[]).
?- avl_tree_planter(Tree,[]).
Я остановлюсь на более простом случае написания обратимого предиката inorder/2
, который генерирует каждое двоичное дерево, имеющее заданный обход обхода. Следующая реализация не завершается, когда ее первый аргумент необоснован:
inorder(leaf,[]).
inorder(node(X,L,R),L3) :-
inorder(L,L1),
inorder(R,L2),
append(L1,[X|L2],L3).
Если вы проследите запрос ?- inorder(Tree,[])
, вы обнаружите, что первый рекурсивный вызов inorder/2
вызывает проблему. Чтобы доказать inorder(Tree,[])
, необходимо доказать бесконечно много целей вида inorder(X,[])
. Как правило, первый рекурсивный вызов inorder/2
предотвращает создание левого поддерева. Это аналогично проблеме левой рекурсии при разборе.
Вот одно из решений. Введем два аргумента, которые отслеживают состояние обхода. Первый аргумент представляет состояние ввода и отслеживает необработанные элементы. Второй представляет состояние вывода и отслеживает остальные элементы. Их разница соответствует элементам, обработанным во время рекурсивного вызова. Отсюда следует, что inorder(Tree,List)
должен быть успешным с состоянием ввода List
и состоянием вывода []
. Вот одна из возможных реализаций:
inorder(Tree,List) :-
inorder(Tree,List,List,[]).
inorder(leaf,[],State,State).
inorder(node(X,L,R),List,[_|State1],State3) :-
inorder(L,Left,State1,State2),
inorder(R,Right,State2,State3),
append(Left,[X|Right],List).
Например:
?- inorder(leaf,List).
List = [].
?- inorder(node(1,leaf,leaf),List).
List = [1].
?- inorder(node(1,node(2,leaf,leaf),leaf),List).
List = [2, 1].
?- findall(Tree,inorder(Tree,[]),Trees).
Trees = [leaf].
?- findall(Tree,inorder(Tree,[1]),Trees).
Trees = [node(1, leaf, leaf)].
?- findall(Tree,inorder(Tree,[1,2]),Trees).
Trees = [node(1, leaf, node(2, leaf, leaf)), node(2, node(1, leaf, leaf), leaf)].
Если эта реализация напоминает вам о разборе, то это потому, что она реализует примерно те же функции, что и следующая определенная пункт грамматики (DCG). Следующий код и обсуждение взаимосвязи между недерминированием и левой рекурсией можно найти в учебнике Маркуса Триски DCG , который я предлагаю прочитать. Использование DCG для обработки списка считается идиоматическим c Пролог.
:- use_module(library(dcg/basics)).
inorder(Tree,List) :-
phrase(inorder(Tree,List,_),List).
inorder(leaf,S,S) -->
[].
inorder(node(X,L,R),[_|S1],S3) -->
inorder(L,S1,S2),
[X],
inorder(R,S2,S3).
Как решить вашу исходную проблему? Чтобы адаптировать эти методы к настройке деревьев AVL, необходимо наложить дополнительные ограничения на генерируемые деревья (т. Е. Выполнить только двоичные деревья поиска, удовлетворяющие свойству AVL). Это не должно быть сложно. Я надеюсь, что вы нашли это объяснение полезным.