I am trying to write a Prolog predicate that gives a possible binary search tree for a given traversal. I chose to represent the tree like t(Root, Left Subtree, Right Subtree), leaves are simply t(Number) and when a subtree does not exist its value is nil.
This is what I have so far (only for postorder traversal in this case):
post(nil, []).
post(t(X), [X]).
post(t(N, L, R), T) :-
post(L, TL), post(R, TR),
append([TL, TR, [N]], T).
This works nicely in one way, but hangs in the other direction:
?- post(t(8, t(5, t(2), nil), t(12, t(9), t(16))), Trav).
Trav = [2, 5, 9, 16, 12, 8].
?- post(Tree, [2, 5, 9, 16, 12, 8]).
Tree = t(8, nil, t(12, nil, t(16, nil, t(9, nil, t(5, nil, t(2)))))) ;
Tree = t(8, nil, t(12, nil, t(16, nil, t(9, nil, t(5, nil, t(2, nil, nil)))))) ;
[execution hangs here]
I realized that post does not require a binary search tree, ie there is no requirement for all nodes in the left subtree to be less than the root node and all nodes in the right subtree to be greater than the root node, so I also wrote this:
tree(t(_)).
tree(nil).
tree(t(N, L, R)) :-
tree(L), tree(R),
( L = t(NL, _, _) -> NL < N ; true ),
( R = t(NR, _, _) -> NR > N ; true ).
I thought I could just do ?- post(Tree, [...]), tree(Tree). to make Prolog only return actual binary search trees. However, I seem to be stuck already at generating possible trees.
How could I improve my code? Is this even doable?
My suggestion is to write different code for different directions. Here is the code to transform a list back into a tree. The main difference with the original code is that we deconstruct the list (with last/3 and append/3) before reconstructing the tree. Note that I added the code to check for a search tree in third clause (the two maplist/2calls) but those can be as well removed if you want to keep it separately.
postlist_to_tree([],nil).
postlist_to_tree([X],t(X)).
postlist_to_tree(Xs,t(Last,LT,RT)):-
last(Fore,Last,Xs),
append(LL,RL,Fore),
maplist('>='(Last),LL),
maplist('=<'(Last),RL),
postlist_to_tree(LL, LT),
postlist_to_tree(RL, RT).
Now to have it as a single predicate, I would suggest to do something like this, using a non-logical predicate (ground/1 in this example) to decide which version to call depending on the instantiation of the arguments at call time.
post2(Tree,List):-
ground(List),
!,
postlist_to_tree(List,Tree).
post2(Tree,List):-
post(Tree,List).
Related
I'm a beginner in Prolog, and my first assignment is to implement a function construct(), that builds a binary tree from a list. I know there is something wrong or missing in my code, but I can't put my finger on it. I also think a helper method may be necessary, but I can't think of how to go about that.
Here is my code so far:
construct([],nil).
construct(E, tree(E,nil,nil)).
construct([H|T], tree(H, construct(T,R), nil)):-
T>H.
construct([H|T], tree(H, construct(T,R), nil)):-
T<H.
First: there are no functions in Prolog, just predicates. So, if you write tree(H, construct(T,R), nil) (from your 2nd clause), then you've created a structure (or "term") that looks like this:
tree
/ | \
/ | \
H construct nil
/ \
/ \
T R
which is not what you want.
Instead, you should write something like this (although this won't do what you want either -- see below):
construct([H|T], tree(H, Tree, nil)):-
T>H,
construct(T, R, Tree).
Second: you should have an easy way to distinguish a value from a non-value. You've chosen nil for non-value, so you could have something like value(V) for storing a value.
Now, think about what a node looks like. It's got 2 parts (left and right), each of which can be either nil, a value or a node. For example, you'd want [1,2,3] to produce a tree that looks something like
node(node(value(1),
value(2)),
value(3))
As for an auxiliary predicate (not "function"), an obvious one is a predicate that inserts a single value to a tree. So, just list out all the possibilities:
insert_value(X, node(nil, nil), node(value(X), nil)). % insert into an empty node
insert_value(X, node(value(Y), nil), node(value(X), value(Y))) :- X < Y.
insert_value(X, node(value(Y), nil), node(value(Y), value(X))) :- X > Y.
insert_value(X, node(node(A,B), value(Y)), node(T1, value(Y))) :-
X < Y,
insert_value(X, node(A,B), T1).
etc.
If you do this, you'll find that this isn't the best representation for a tree, but I'll leave it to you to come up with a better representation. (Incidentally, there's no need for all the node functors to have 2 elements; it's ok to have node - contains nothing; node(X) - contains a single item; node(X,Y) - contains 2 items).
Also, you'll need to decide what to do wwhen inserting a value that's already in the tree -- does insert_value/3 fail, does it just do nothing, or does it allow multiple identical values?
Once you've got this working, then you can write a predicate to insert a list of values:
insert_values([], Tree, Tree).
insert_values([X|Xs], Tree0, Tree) :-
insert_value(X, Tree0, Tree1),
insert_values(Xs, Tree1, Tree).
This is a very common pattern; the general form of it is called "foldl".
And finally, get the whole thing going with an initial value of an empty node:
insert_values(Values, Tree) :-
insert_values(Values, node(nil,nil), Tree).
It's also very common in Prolog to define predicates with different numbers of arguments; in this case, insert_values/2 uses insert_values/3 with an initial value of an empty node (node(nil,nil)).
In Prolog, we can use:
the atom nil to represent an empty binary tree, and
a term of the form t(Left, Root, Right) to represent a non-empty binary tree, where Root is the value at the root of the tree, and Left and Right are also terms representing binary trees.
To make the binary trees easier to see, you can use the following predicate:
show(T) :-
show(T, 0).
show(nil, _).
show(t(Left, Root, Right), Indent) :-
Indent1 is Indent + 3,
show(Right, Indent1),
format('~*c~w\n', [Indent, 32, Root]),
show(Left, Indent1).
Example:
?- show( t(t(nil,1,nil), 2, t(nil,3,nil)) ).
3
2
1
true.
Let List be a list denoting the in-order traversal of an arbitrary binary tree, as illustrated bellow:
Then, since different binary trees can have the same in-order traversal, a binary tree corresponding to such list can be described as follows:
% convert from in-order traversal list to binary tree
list_to_bt(List, Tree) :-
( List = []
-> Tree = nil
; Tree = t(Left, Root, Right),
append(Prefix, [Root|Suffix], List),
list_to_bt(Prefix, Left),
list_to_bt(Suffix, Right) ).
Example:
?- list_to_bt([1,2,3], T), show(T).
3
2
1
T = t(nil, 1, t(nil, 2, t(nil, 3, nil))) ;
3
2
1
T = t(nil, 1, t(t(nil, 2, nil), 3, nil)) ;
3
2
1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil))
...
false.
If you want to obtain only balanced binary trees (e.g., trees such that, for each node, the absolute difference in the sizes of the right and left subtrees is at most 1), then you can include this constraint as follows:
% convert from in-order traversal list to balanced binary tree (bbt)
list_to_bbt(List, Tree) :-
( List = []
-> Tree = nil
; Tree = t(Left, Root, Right),
append(Prefix, [Root|Suffix], List),
length(Prefix, Size1),
length(Suffix, Size2),
abs(Size1 - Size2) =< 1,
list_to_bbt(Prefix, Left),
list_to_bbt(Suffix, Right) ).
Example:
?- list_to_bbt([1,2,3], T), show(T).
3
2
1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil)) ;
false.
If you want only balanced binary search trees from an arbitrary list, then you must sort this list before creating the balanced binary tree:
% convert from arbitrary list to balanced binary search tree (bbst)
list_to_bbst(List, Tree) :-
sort(List, Sorted),
list_to_bbt(Sorted, Tree).
Examples:
?- list_to_bbst([3,1,7,5,4,2,6], T), show(T).
7
6
5
4
3
2
1
T = t(t(t(nil, 1, nil), 2, t(nil, 3, nil)), 4, t(t(nil, 5, nil), 6, t(nil, 7, nil))) ;
false.
?- list_to_bbst([3,1,4,2], T), show(T).
4
3
2
1
T = t(t(nil, 1, nil), 2, t(nil, 3, t(nil, 4, nil))) ;
4
3
2
1
T = t(t(nil, 1, nil), 2, t(t(nil, 3, nil), 4, nil)) ;
4
3
2
1
T = t(t(nil, 1, t(nil, 2, nil)), 3, t(nil, 4, nil)) ;
4
3
2
1
T = t(t(t(nil, 1, nil), 2, nil), 3, t(nil, 4, nil)) ;
false.
My prolog program is supposed take a binary tree and make a new tree that adds a child to any node that only has one child so that all nodes have 2 or zero children. The binary tree representation being used is along the lines of t(73,t(31,t(5,nil,nil),nil),t(101,t(83,nil,t(97,nil,nil)),nil)). The issue is that it only fixes the nodes that have a missing right child and its not working for nodes that are missing a left node.
treeEx(X) :-
X = t(73,t(31,t(5,nil,nil),nil),t(101,t(83,nil,t(97,nil,nil)),nil)).
singleFill(t(_,nil,nil),_,_):-!.
singleFill(t(Root,nil,Right),V,t(Root,t(V,nil,nil),Right)):-
singleFill(Right,V,_).
singleFill(t(Root,Left,nil),V,t(Root,Left,t(V,nil,nil))):-
singleFill(Left,V,_).
singleFill(t(Root,Left,Right),V,t(Root,LD,RD)):-
singleFill(Left,V,LD),
singleFill(Right,V,RD).
When using the query treeEx(T),singleFill(T,0,L). it should produce the result:
T = t(73, t(31, t(5, nil, nil), nil), t(101, t(83, nil, t(97,
nil, nil)), nil)),
L = t(73, t(31, t(5, nil, nil), t(0, nil, nil)), t(101, t(83,
t(0, nil, nil), t(97, nil, nil)), t(0, nil, nil)))
however mine produces:
T = t(73, t(31, t(5, nil, nil), nil), t(101, t(83, nil, t(97, nil,
nil)), nil)),
L = t(73, t(31, t(5, nil, nil), t(0, nil, nil)), t(101, t(83, nil,
t(97, nil, nil)), t(0, nil, nil))) .
The problem is that the node with 83 has one child but its not adding the zero. When I traced it I noticed that it was because there are 2 separate reclusive calls that have 2 different forms of the tree so I think that only the nodes with right children are saving the changes to the new tree. However the changes to the left tree are not
So, right off the bat we have a few weird things about your code:
?- singleFill(t(1,nil,nil), 0, Y).
true.
It's weird to me that this is true but didn't bind anything on Y. This must be the behavior that you noticed. I think it may stem from this:
?- singleFill(nil, 0, Y).
false.
Namely, your code doesn't handle the base case of the recursion. This is why you seem to need so many cases. Always check your predicates with simple inputs close to the base cases, those are the ones that will help you see the problem. In actuality, you only need three cases:
single_fill(nil, New, t(New, nil, nil)).
single_fill(t(V, nil, nil), _, t(V, nil, nil)) :- !.
single_fill(t(V, Left, Right), New, t(V, NewLeft, NewRight)) :-
single_fill(Left, New, NewLeft),
single_fill(Right, New, NewRight).
Notice that I'm only ignoring one variable here, and it's the fill variable in the case where no filling happens.
I still have a cut here, which I'm not happy about. Without the cut, the first solution matches your specification but you get several more, basically one for each case where there was nil in both branches in the tree. With the cut, this predicate will successfully "check" those inputs even though it wouldn't generate them, which is a violation of backwards correctness. This suggests to me that there may be a better way to write this that doesn't have this problem. But I couldn't think of it. I feel like it should be sufficient to say Left \= nil, Right \= nil instead but that didn't work.
I am trying to build a predicate treeMerge(A,B,C) that returns true if C is the result of merging the two trees A and B. Any suggestions on how I can implement this? I have a rough idea, I am thinking of merging the root, then first child then second and so on, but I am fairly new to prolog.
The idea is to get the list of the nodes inside a tree then add each nodes in the other tree. I've developed a predicate merge/3 which merges the two tree as input in a third tree (insert all the nodes of the first tree into the second). A tree is represented as t(Node,Left,Right). Here is the code:
getNodes(nil,[]).
getNodes(t(E,L,R),[E|S]) :-
append(SL,SR,S),
getNodes(L,SL),
getNodes(R,SR),!.
insert(Node,nil,t(Node,nil,nil)).
insert(Node,t(El,Left,Right),t(El,L,R)):-
(Node > El ->
insert(Node,Right,R),
L = Left
; insert(Node,Left,L),
R = Right).
insertNodesList([],T,T).
insertNodesList([H|T],Tree,M):-
insert(H,Tree,T1),
insertNodesList(T,T1,M).
merge(T1,T2,Merged):-
getNodes(T1,LNodesT1),
insertNodesList(LNodesT1,T2,Merged).
Query:
?-merge(t(3,t(2,t(1,nil,nil),nil),t(4,nil,t(7,nil,t(5,nil,nil)))),t(3,t(2,nil,nil),t(4,nil,t(7,nil,t(5,nil,nil)))),T).
T = t(3, t(2, t(2, t(1, nil, nil), nil), t(3, nil, nil)), t(4, t(4, nil, nil), t(7, t(7, t(5, nil, nil), nil), t(5, nil, nil))))
Example of the node looks like:
node(3, nil, 14).
node(14, nil, 15).
node(15, nil, 92).
I have seen similar questions asked here however mine is different as my nodes have 3 instead of 2 values in the parameter.
Example of how it should run:
?- inOrder(3, X).
X = [3, 14, 15, 35, 65, 89, 92] .
My current code is:
% the in-order traversal of a leaf is the leaf
inOrder(X, [X]) :-
node(X, nil, nil).
% if the node only has a left child, we traverse that
inOrder(x, [X|T]) :-
node(X, L, [X|T]),
inOrder(L, T).
% if the node only has a right child we traverse that
inOrder(x, [X|T]) :-
node(X, nil, R),
inOrder(R, T).
% if the node has both, we traverse them both.
inOrder(x, [X|T]) :-
node(L, X, R),
L \= nil, R \= nil,
inOrder(L, T1),
inOrder(R, T2),
append(T1, T, T2).
It returns false instead of an actual value.
There are several twists in your representation. In general, treelike structures are not flattened out in the database, here with node/3, but rather maintained in a single term.
Also, it seems to be a good idea to insist that each node has its own fact. In your example 92 needs a fact!
So given all these precautions one can write, using DCGs:
node(3, nil, 14).
node(14, nil, 15).
node(15, nil, 92).
node(92, nil, nil). % added!
inorder(I, Xs) :-
phrase(inorder(I), Xs).
inorder(nil) -->
[].
inorder(I) -->
{dif(I, nil)},
{node(I, L, R)},
inorder(L),
[I],
inorder(R).
?- inorder(3,L).
L = [3,14,15,92]
; false.
The check your database for orphaned nodes:
orphan(Nr) :-
node(_, L, R),
( Nr = L ; Nr = R ),
dif(Nr, nil),
\+ node(Nr, _, _).
So orphan(N) should fail on your database.
To get rid of the leftover choicepoint ; false. add the following rule in front of inorder//1:
inorder(Nil) --> {Nil == nil}, !.
You are using a lower-case x in your recursive cases for inOrder; that should be a variable. But that probably isn't the only problem.
I am studying binary tree in Prolog and I have some problem to add leaves to a specific b-tree.
I have the following predicates that add a single leaf to a b-tree:
addLeaf(nil, X, t(nil, X, nil)).
addLeaf(t(Left, X, Right), X, t(Left, X, Right)).
addLeaf(t(Left, Root, Right), X, t(Left1, Root, Right)) :-
gt(Root, X), addLeaf(Left, X, Left1).
addLeaf(t(Left, Root, Right), X, t(Left, Root, Right1)) :-
gt(X, Root), addLeaf(Right, X, Right1).
gt(Element1, Element2) :- Element1 #> Element2.
This is pretty simple and I think that I have not problem with it.
My problem is related about to use it to perform query that add more then a single leaf to a bin tree,
For example if, in the Prolog shell, I perform the following statement:
[debug] ?- addLeaf(nil, 6, Tree).
Tree = t(nil, 6, nil).
the original tree is nil (there is no tree), so add a new leaf is equivalent to create a new tree (called Tree) that have the element 6 as root.
Now my problem is: "I have create a new tree, that corresponds to the Tree variable, what have I to do to add others leaves to this tree?"
Because, if now I try to perform the following statement:
[debug] ?- addLeaf(Tree, 6, NewTree).
Tree = nil,
NewTree = t(nil, 6, nil)
I obtain that Tree = nill (also if I had just created it in the previous statement. Evidently the Tree variable (in the two statement) are independent of each other, evidently the variables are independent of each other.
So I try to perform:
[debug] ?- addLeaf(nil, 6, Tree), addLeaf(Tree, 8, NewTree).
Tree = t(nil, 6, nil),
NewTree = t(nil, 6, t(nil, 8, nil)).
and it work fine adding the 8 element as the right child of 6 (according to the b-tree rules)
But, I am asking if, in the Prolog shell, is it possible do something this:
create new tree.
add a leaf.
add another leaf.
add another leaf.
...
...
without declare all the operation in an unique statement, some idea about it?
?- addLeaf(nil, 6, Tree).
Tree = t(nil, 6, nil).
?- addLeaf($Tree, 7, NewTree).
NewTree = t(nil, 6, t(nil, 7, nil)).
?- addLeaf($NewTree, 4, NewTree2).
NewTree2 = (t(nil, 4, nil), 6, t(nil, 7, nil)) .
this sample uses top level variables (a feature of SWI-Prolog). Judging from NewTree2, it seems you have a typo in your code.
You can reuse top-level bindings in SWI Prolog:
6 ?- X = a(_).
X = a(_G236)
7 ?- Z = $X.
Z = a(_G269)