Related
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.
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.
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).
I would like to ask, if anyone knows how to improve (if it's not optimal) this code.
The idea, is that you have a list of elements, and I want to return a list, with two sublists inside it, the first sublist should contain the elements that are contained in the odd positions of the list, and the second sublist should contain, the elements that are contained in the even positions of the list.
Some examples:
?-evenAndOdd([1,2,3,4,5],[[1,3,5],[2,4]])
True.
?-evenAndOdd([a,b,c,d,e],[[a,c,e],[b,d]]).
True.
The code I have implemented is the next one:
evenAndOdd([],[]).
evenAndOdd([H|R],NL):-
evenAndOddRec([H|R], [[],[]],1,NL).
evenAndOddRec([], [LOdd,LEven],_,[LOdd,LEven]).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
\+ even(Pos),
!,
NPos is Pos +1,
append(LOdd,[H],NLOdd),
evenAndOddRec(R,[NLOdd,LEven],NPos,NL).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
NPos is Pos + 1,
append(LEven, [H], NLEven),
evenAndOddRec(R,[LOdd, NLEven],NPos,NL).
even(N):-
N mod 2 =:=0.
One symptom that the code is not optimal is that it will run off into the woods if you ask for an additional solution in the -,+,+ instantiation pattern:
?- evenAndOdd(X, [[1,3,5], [2,4,6]]).
X = [1, 2, 3, 4, 5, 6] ;
<time passes>
This kind of thing is a frequent occurrence when manually trying to match up lists with indexes in Prolog.
Stylistically, I would rather not give back a list containing exactly two lists when I could just have three arguments instead of two; this is, after all, a relationship between three lists, the combined list and the even and odd items.
Additionally, just eyeballing it, I'm not sure why any arithmetic or any cuts are needed here. This is how I would implement it:
evenAndOdd([], [], []).
evenAndOdd([O], [O], []).
evenAndOdd([O,E|Rest], [O|ORest], [E|ERest]) :- evenAndOdd(Rest, ORest, ERest).
This works with many instantiations:
?- evenAndOdd([1,2,3,4,5,6], O, E).
O = [1, 3, 5],
E = [2, 4, 6].
?- evenAndOdd([1,2,3,4,5], O, E).
O = [1, 3, 5],
E = [2, 4] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4]).
X = [1, 2, 3, 4, 5] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4,6]).
X = [1, 2, 3, 4, 5, 6].
?- evenAndOdd(X, [1,3,5], [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6]).
X = [1, 3, 5].
You can implicitly determine even and odd values upon recursion, by taking two elements at a time (and taking into account when the has an odd number of elements):
evenAndOdd(L, [LOdd, LEven]):-
evenAndOdd(L, LOdd, LEven).
evenAndOdd([], [], []).
evenAndOdd([Odd], [Odd], []).
evenAndOdd([Odd,Even|Tail], [Odd|LOdd], [Even|LEven]):-
evenAndOdd(Tail, LOdd, LEven).
A good language for logic programming should allow the programmer to use a language close to the language used by the mathematicians. Therefore, I have always considered the lack of proper universal quantifier in Prolog an important shortcoming.
Today an idea came to me how to define something much better than forall and foreach.
forany(Var, {Context}, Condition, Body)
This predicate tries to prove Body for all instantiations Var gets successively on backtracking over Condition. All variables in Condition and Body are considered local unless listed in Var or Context. Condition is not permitted to modify in any way the variables listed in Context, otherwise forany won't work correctly.
Here is the implementation (based on yall):
forany(V, {Vars}, Goal1, Goal2) :-
( bagof(V, {V,Vars}/Goal1, Solutions)
-> maplist({Vars}/[V]>>Goal2, Solutions)
; true ).
My first question is about the second argument of forany. I'd like to eliminate it.
Now some examples
Construct a list of the first 8 squares:
?- length(X,8), forany(N, {X}, between(1,8,N),
(Q is N*N, nth1(N, X, Q))).
X = [1, 4, 9, 16, 25, 36, 49, 64].
Reverse a list:
?- X=[1,2,3,4,5], length(X,N), length(Y,N),
forany(I, {X,Y,N}, between(1,N,I),
(J is N-I+1, nth1(I,X,A), nth1(J,Y,A))).
X = [1, 2, 3, 4, 5],
N = 5,
Y = [5, 4, 3, 2, 1].
Subset:
subset(X, Y) :- forany(A, {X,Y}, member(A,X), member(A, Y)).
A funny way to generate all permutations of a list without duplicates:
permutation(X, Y) :-
length(X, N), length(Y, N), subset(X, Y).
?- permutation([1,2,3],X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
A funny way to sort a list of different integers. Notice that constraints are used to make the list sorted so most permutations won't be generated:
sorted(X) :- forany(A-B, {X}, append(_, [A,B|_], X),
A#<B).
?- X=[7,3,8,2,6,4,9,5,1], length(X, N), length(Y, N),
sorted(Y), subset(X,Y).
X = [7, 3, 8, 2, 6, 4, 9, 5, 1],
N = 9,
Y = [1, 2, 3, 4, 5, 6, 7, 8, 9] .
The problem
It seems that this forany works brilliantly when constraints are not used. Also, it can be used to generate constraints, but at least on SWI-Prolog there are problems when constraints already have been generated. The reason for this is that forany uses bagof and according to the manual of SWI-Prolog:
Term-copying operations (assertz/1, retract/1, findall/3, copy_term/2, etc.) generally also copy constraints. The effect varies from ok, silent copying of huge constraint networks to violations of the internal consistency of constraint networks. As a rule of thumb, copying terms holding attributes must be deprecated. If you need to reason about a term that is involved in constraints, use copy_term/3 to obtain the constraints as Prolog goals, and use these goals for further processing.
Here is a demonstration of the problem bagof creates with constraints:
?- X=[A,B,C], dif(C,D), bagof(_, K^member(K,X), _).
X = [A, B, C],
dif(C, _5306),
dif(C, _5318),
dif(C, _5330),
dif(C, D).
As you can see, three unnecessary constraints are created.
My second question is if this is a problem only of SWI-Prolog.
And the third question: is there a way to fix this in SWI-Prolog. The above quote from the manual suggests that copy_term/3 should be used. Unfortunately, I don't understand this suggestion and I don't know if it is useful for forany.
Great news! I was surprised that bagof is written in Prolog. By looking at its code I learned that some things I thought are impossible are in fact possible. And just as the manual of SWI-Prolog suggested, copy_term/3 or rather the similar predicate copy_term_nat/2 helped.
So with great joy I am able to present a fully working (as far as I can tell) universal quantifier for SWI-Prolog:
forany(V, {Vars}, Condition, Body) :-
findall(V-Vars, Condition, Solutions),
% For SWI-Prolog. Can be replaced by Solutions=Clean_solutions in other systems
copy_term_nat(Solutions, Clean_solutions),
forany_execute_goals(Clean_solutions, Vars, V, Body).
forany_execute_goals([], _, _, _).
forany_execute_goals([Sol-NewVars|Solutions], Vars, V, Body) :-
% The following test can be removed
assertion(subsumes_term(NewVars, Vars)),
% or replaced by the following more standard use of throw/1:
% ( subsumes_term(NewVars, Vars)
% -> true
% ; throw('Forbidden instantiation of context variables by the antecedent of forany') ),
NewVars = Vars,
call({Vars}/[V]>>Body, Sol),
forany_execute_goals(Solutions, Vars, V, Body).