Creating Binary Tree in Prolog - prolog

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.

Related

convert list of adjacencies to recursive format

Given a binary tree in a format of list of adjacencies such as [1-8,1-2,...]
The elements in the list are not in any particular order.
Given the root of the tree the list needs to be converted to a recursive format t(L,Root,R). Where L and R are trees themselves or nil.
This is what I have tried:
% convert binary tree from adjacencies list to recursive term format.
% make_tree(AdjList, Root, Tree)
make_tree([],Root,t(nil,Root,nil)):-!.
make_tree([Root-X],Root,t(X,Root,nil)).
make_tree([Root-X],Root,t(nil,Root,X)):-!.
make_tree(_,nil,nil):-!.
make_tree(N,Root,t(L,Root,R)):-
find_kids(N,Root,C,S),
reorder(C,[C1,C2]),
make_tree(S,C1,L), make_tree(S,C2,R).
% reorder 2 out of 2
reorder([X,Y],[Y,X]).
reorder([X,Y],[X,Y]).
% find children and remove them from list of nodes
find_kids(L,Root,[C1,C2],R):-
find_children(L,Root,[C1,C2|_],R).
find_children([Root-Y|Xs],Root,[Y|T],Acc):-
find_children(Xs,Root,T,Acc).
find_children([X-Y|Xs],Root,R,[X-Y|Acc]):-
X \= Root,
find_children(Xs,Root,R,Acc).
find_children([],_,[nil,nil],[]).
Consider the following binary tree and an arbitrary adjacency list for it:
% 0
% / \
% 1 2
% / \ / \
% 3 4 5 6
% / \ \
% 7 8 9
adj_list([1-3,0-2,1-4,5-9,3-7,2-5,2-6,3-8,0-1]).
To get the children of a node, you can use the following predicate:
find_children(Root, List, Children, Rest) :-
partition({Root}/[X-_]>>(X=Root), List, Children0, Rest),
maplist([Root-Child, Child]>>true, Children0, Children).
Examples:
?- adj_list(List), find_children(3, List, Children, Rest).
List = [1-3, 0-2, 1-4, 5-9, 3-7, 2-5, 2-6, 3-8, ... - ...],
Children = [7, 8],
Rest = [1-3, 0-2, 1-4, 5-9, 2-5, 2-6, 0-1].
?- adj_list(List), find_children(5, List, Children, Rest).
List = [1-3, 0-2, 1-4, 5-9, 3-7, 2-5, 2-6, 3-8, ... - ...],
Children = [9],
Rest = [1-3, 0-2, 1-4, 3-7, 2-5, 2-6, 3-8, 0-1].
?- adj_list(List), find_children(4, List, Children, Rest).
List = Rest, Rest = [1-3, 0-2, 1-4, 5-9, 3-7, 2-5, 2-6, 3-8, ... - ...],
Children = [].
To build all possible binary trees from a given root, you can use the following code:
% trees(+Root, -Tree)
trees(Root, Tree) :-
adj_list(List),
make_tree(Root, List, _Rest, Tree).
% make_tree(+Root, +List, -Rest, -Tree)
make_tree(Root, List, Rest, Tree) :-
find_children(Root, List, Children, Rest0),
make_tree(Children, Root, Rest0, Rest, Tree).
make_tree([], Root, List, List, t(nil, Root, nil)).
make_tree([X], Root, List, Rest, Tree) :-
make_tree(X, List, Rest, Subtree),
( Tree = t(Subtree, Root, nil)
; Tree = t(nil, Root, Subtree) ).
make_tree([X,Y], Root, List, Rest, Tree) :-
make_tree(X, List, Rest0, Subtree1),
make_tree(Y, Rest0, Rest, Subtree2),
( Tree = t(Subtree1, Root, Subtree2)
; Tree = t(Subtree2, Root, Subtree1) ).
Examples:
?- trees(4, T).
T = t(nil, 4, nil).
?- trees(3, T).
T = t(t(nil, 7, nil), 3, t(nil, 8, nil)) ;
T = t(t(nil, 8, nil), 3, t(nil, 7, nil)).
?- trees(5, T).
T = t(t(nil, 9, nil), 5, nil) ;
T = t(nil, 5, t(nil, 9, nil)) ;
false.
?- trees(2, T).
T = t(t(t(nil, 9, nil), 5, nil), 2, t(nil, 6, nil)) ;
T = t(t(nil, 6, nil), 2, t(t(nil, 9, nil), 5, nil)) ;
T = t(t(nil, 5, t(nil, 9, nil)), 2, t(nil, 6, nil)) ;
T = t(t(nil, 6, nil), 2, t(nil, 5, t(nil, 9, nil))) ;
false.

Find binary search trees based on traversal in prolog

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).

Prolog subgroup of list of size n

I'm trying to create a rule to determine if a list is a sublist of size n of another list.
isSubgroup/3
isSubgroup(+Subgroup, +Group, +N)
For example, isSubgroup([1, 2, 4], [1, 2, 3, 4, 5], 3) would return True
However, isSubgroup([4, 2, 1], [1, 2, 3, 4, 5], 3) would return False (because of the different order)
I thought of checking for each member of the subgroup whether or not it's a member of the large group, but that would ignore the order.
Is the idea feasible?
Really, try to write an inductive relation. Meanwhile, library(yall) coupled with library(apply) can make one liner:
isSubgroup(S,G,N) :- length(S,N),
foldl({G}/[E,P,X]>>(nth1(X,G,E),X>=P),S,1,_F).
As #WillemVanOnsem suggested, an inductive solution:
subGroups([], []).
subGroups([X|Xs], [X|Ys]):-
subGroups(Xs, Ys).
subGroups(Xs, [_|Ys]):-
subGroups(Xs, Ys).
subGroupsN(Options, N, Solution) :-
length(Solution, N),
subGroups(Solution, Options).
We can define this predictate by an inductive definition. A Subgroup is a subgroup of Group if:
the Subgroup is an empty list;
the first element of the Subgroup is the same as the first element of Group, and the rest of the Subgroup is a subgroup of the rest of the Group;
the Subgroup is a subgroup of the rest of the Group.
We need to update N accordingly such that, if the Subgroup is empty, then the length is 0:
isSubgroup([], _, 0). %% (1)
isSubgroup([H|TS], [H|TG], N) :- %% (2)
N1 is N-1,
isSubgroup(TS, TG, N1).
isSubgroup(S, [_|TG], N) :- %% (3)
isSubgroup(S, TG, N).
The above however results in duplicate trues for the same subgroup. This is due to the fact that we can satisfy the predicate in multiple ways. For example if we call:
isSubgroup([], [1,2], 0).
then it is satisfied through the fact (1), but the last clause (3) also calls this with isSubgroup([], [1], 0)., that will then get satisfied through the fact (1), etc.
We can avoid this by making the last clause more restrictive:
isSubgroup([], _, 0). %% (1)
isSubgroup([H|TS], [H|TG], N) :- %% (2)
N1 is N-1,
isSubgroup(TS, TG, N1).
isSubgroup([HS|TS], [_|TG], N) :- %% (3)
isSubgroup([HS|TS], TG, N).
The above works for the given "directions" (all arguments should be grounded, are "input"). But typically one wants to use a predicate in other directions as well. We can implement a version that works basically when we use arguments as "output" as well, and still make use of tail-call optimization (TCO):
isSubgroup(S, G, N) :-
isSubgroup(S, G, 0, N).
isSubgroup([], _, L, L). %% (1)
isSubgroup([H|TS], [H|TG], L, N) :- %% (2)
L1 is L+1,
isSubgroup(TS, TG, L1, N).
isSubgroup([HS|TS], [_|TG], L, N) :- %% (3)
isSubgroup([HS|TS], TG, L, N).
For example:
?- isSubgroup([1,4,2], G, N).
G = [1, 4, 2|_2974],
N = 3 ;
G = [1, 4, _2972, 2|_2986],
N = 3 ;
G = [1, 4, _2972, _2984, 2|_2998],
N = 3 ;
G = [1, 4, _2972, _2984, _2996, 2|_3010],
N = 3 .
Here Prolog is thus able to propose groups for which [1,4,2] is a subgroup, and it is capable to determining the length N of the subgroup.
We can query in the opposite direction as well:
?- isSubgroup(S, [1,4,2], N).
S = [],
N = 0 ;
S = [1],
N = 1 ;
S = [1, 4],
N = 2 ;
S = [1, 4, 2],
N = 3 ;
S = [1, 2],
N = 2 ;
S = [4],
N = 1 ;
S = [4, 2],
N = 2 ;
S = [2],
N = 1 ;
false.
Prolog can, for a given group [1,4,2] enumerate exhaustively all possible subgroups, together with N the length of that subgroup.

How to add a leaves to a tree in Prolog

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)

Sort a list of cards prolog

I have a list of card structures such as:
[card(ace, spades), card(10, diamonds), card(king, clubs)]
can anyone help me understand how to sort these according to face value?
I have this:
bubblesort(L, L1) :-
( bubble(L, L2)
-> bubblesort(L2, L1)
; L = L1 ).
bubble([card(A,A2), card(B,B2)|T], L) :-
( A > B
-> L = [card(B,B2), card(A,A2)|T]
; L = [card(A,A2) | L1],
bubble([card(B,B2)|T], L1)).
which works well (its bubble sort) except when you have card(ace, spades) or alike because ace is not a number
You can use predsort/3
It's like sort/2, but determines the order of the terms by calling the comparison predicate you fed it. So we only need to write a compare_values/3 predicate that compares the face values of your cards. My try:
compare_values(D, card(A,_), card(B,_)) :-
nth0(X, [ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, jack, queen, king], A),
nth0(Y, [ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, jack, queen, king], B),
compare(D, X, Y).
sort_cards(L, R) :-
predsort(compare_values, L, R).
Explanation of the compare_values/3 predicate:
We need to define an ordering over the following list:
[ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, jack, queen, king]
how? Given two values A and B, we simply use nth0/3 to search for A and B in the list. nth0/3 will give us the position of the element we are searching for. So now:
X = position of the element A in the ordered list
Y = position of the element B in the ordered list
but now X and Y are guaranteed to be numbers! And we can compare them with the built-in predicate compare/3. If X < Y the card A comes before the card B, and vice-versa.
compare/3 will compare X and Y, and return one of (>), (<), (=).
An example:
?- compare_values(D, card(ace, clubs), card(7, spades)).
nth0 search for ace and 7 in the list of ordered values.
Now X = 0 and Y = 6 (the indexes of ace and 7 in the list)
compare(D, 0, 6) unifies with D = (<)
Finally: the predsort/3 predicate uses compare_values to sort the list accordingly to the order defined by compare_values/3
A query:
?- sort_cards([card(king, spades), card(ace,spades), card(3, clubs), card(7,diamonds), card(jack,clubs)], X).
X = [card(ace, spades), card(3, clubs), card(7, diamonds), card(jack, clubs), card(king, spades)].

Resources