We represent the empty tree by the atom 'nil' and the non-empty tree by the term t(X,L,R), where X denotes the root node and L and R denote the left and right subtree, respectively.
As such, a predicate that tests if a term represents a binary tree is as follows:
is_tree(nil).
is_tree(t(_, Left, Right)) :-
is_tree(Left), is_tree(Right).
And it works great, for example:
% ?- is_tree(nil).
%@ true.
% ?- is_tree(t(a, nil, nil)).
%@ true.
% ?- is_tree(t(a, t(b, nil, nil), nil)).
%@ true.
However, when posed with a general query, it only seems to create right side nodes:
% ?- is_tree(X).
%@ X = nil
%@ ; X = t(_A,nil,nil)
%@ ; X = t(_A,nil,t(_B,nil,nil))
%@ ; ... .
Now, if X was a list, I could have just prepended the query with length(X, _)
to get iterative deepening. However, seeing as the argument is not a list, I am at a loss on how to get a fair enumeration.
I tried to make a predicate that calculates the depth of the tree:
tree_depth(nil, 0).
tree_depth(t(_, Left, Right), Depth) :-
tree_depth(Left, D0), tree_depth(Right, D1),
Depth #= max(D0, D1) + 1.
And then tried to limit it with a constraint, but all I got was a nonterminating query, which, upon inspection, just keeps on generating deeper and deeper trees with only right hand subtrees:
% ?- Y #< 3, tree_depth(X, Y), is_tree(X).
%@ Y = 0, X = nil
%@ ; Y = 1, X = t(_A,nil,nil)
%@ ; Y = 2, X = t(_A,nil,t(_B,nil,nil))
%@ ; *hangs*
At this point I am unsure what should I do, nor what am I doing wrong. I am using scryer prolog if it matters.
The problem is the left recursion before the constraint. A predicate is left recursive and not tail recursive, if the first goal in a clause is the recursive call:
p :- p, ...
If you can stop the recursion before the left recursive call, you could be more lucky. The Depth parameter will give the max depth, and it will generate also smaller trees:
tree_enum(nil, _).
tree_enum(t(_,Left,Right), Depth) :-
Depth > 0, %%% stop condition
Depth2 is Depth-1,
tree_enum(Left, Depth2),
tree_enum(Right, Depth2).
Here is an example run:
?- tree_enum(X, 2).
X = nil ;
X = t(_, nil, nil) ;
X = t(_, nil, t(_, nil, nil)) ;
X = t(_, t(_, nil, nil), nil) ;
X = t(_, t(_, nil, nil), t(_, nil, nil)) ;
false.