Related
I need to count all the internal nodes of a binary tree using prolog I can count all of the nodes using the following code
internal(tree(_,L,R), I) :- internal(L, I2), internal(R, I3), I is I2 + I3 + 1.
internal(nil, 0).
And I thought that by changing the base case to
internal(tree(_,nil, nil), 0).
I could get it to work but it returns false.
here is a test case that should return 4 internal(tree(8,tree(5,tree(2,nil,nil),tree(7,nil,nil)), tree(9,nil,tree(15,tree(11,nil,nil),nil))),I).
Could anyone tell me where my mistake is?
Thanks
After reading your suggestions I've got this but it still fails.
internal(tree(_,L,R), I) :- internal(L, I2), internal(R, I3), I is I2 + I3.
internal(tree(_,nil, R), I):- !, internal(R, I3), I is I3 + 1.
internal(tree(_,L, nil), I):- !, internal(L, I3), I is I3 + 1.
internal(tree(_,nil, nil), 0).
internal(nil, 0).
If you change the predicate to:
internal(tree(_,nil, nil), 0).
internal(tree(_,L,R), I) :- internal(L, I2), internal(R, I3), I is I2 + I3 + 1.
then this will fail for trees with a nil child and a non-nil child this will fail.
Indeed: if the tree is tree(1, nil, tree(2, nil, nil)), then Prolog will first try to satisfy the base case, but sine nil is not equal to a tree(_, nil, nil), that fails. Next it aims to satisfy the recursive case, and first unifies L = nil, and R = tree(2, nil, nil). Now it calls internal(L, I2), but since internal(nil, I1) can not be satisfied it fails.
We can thus first construct a predicate that satisfies if the two subtrees result in an internal node:
isinternal(tree(_, _, _), _).
isinternal(_, tree(_, _, _)).
so this predicate succeeds if at least one of the subtrees is a tree(_, _, _).. Now we can use this predicate to count the number of internal nodes:
internal(nil, 0).
internal(tree(_, nil, nil), 0).
internal(tree(_, L, R), I) :-
isinternal(L, R),
internal(L, NL),
internal(R, NR),
I is NL + NR + 1.
The above can be improved in terms of readability. I leave this as an exercise.
List is the list of values in leaf nodes of a binary tree and I am trying to figure out how to output just that. This is giving me all the nodes but I need just the leaves.
lea(nil,[]).
lea(t(X,L,R),[X|L]) :-
lea(L,L1),
lea(R,L2),
append(L1,L2,L).
Running this gives me:
?- lea(t(a,t(b,t(d,nil,nil),t(e,nil,nil)),t(c,nil,t(f,t(g,nil,nil),nil))),
List).
List = [a, b, d, e, c, f, g]
but I need
List = [d, e,g]
Is it possible.
Let's use a DCG - a Definite Clause Grammar. We start with your original definition:
lea(T, L) :-
phrase(values(T), L).
values(nil) -->
[].
values(t(X,L,R)) -->
[X],
values(L),
values(R).
Now, we need to restrict ourselves to those t/3 that are leaves. One possibility is to enumerate all cases:
lea2(T, L) :-
phrase(leaves(T), L).
leaves(nil) -->
[].
leaves(t(X,nil,nil)) -->
[X].
leaves(t(_,L,R)) -->
{ dif(L+R,nil+nil) },
leaves(L),
leaves(R).
It would be even better and more efficient to use a conditional construct similar to if_/3. I want to leave this to someone interested.
First, we extend if_/3 to work with DCG's:
if_(C_1, Then_0, Else_0) --> % if_//3
{ call(C_1, Truth) },
{ functor(Truth, _, 0) }, % safety check
( { Truth == true } -> phrase(Then_0)
; { Truth == false }, phrase(Else_0)
).
Using if_//3 and (=)/3 we can handle non-nil tree nodes with one clause (instead of two):
lea3(T, Ls) :-
phrase(leaves(T), Ls).
leaves(nil) --> [].
leaves(t(X,L,R)) -->
if_(L-R = nil-nil, [X], []),
leaves(L),
leaves(R).
The same solution, not so far from first implementation, can be expressed as:
lea(nil, []).
lea(t(X, nil, nil), [X]).
lea(t(_, A, B), L) :-
lea(A, L1),
lea(B, L2),
append(L1, L2, L)
L \= [].
The last row (L \= []) can be removed (if you accept the possibility of find every solution).
I have this Prolog predicate for PreOrder traversal of a tree:
preOrder(nil, []).
preOrder(node(X, nil, nil), [X]).
preOrder(node(X, L, _), [X|T]) :- preOrder(L, T).
preOrder(node(X, _, R), [X|T]) :- preOrder(R, T).
The problem is, it returns an incomplete list. For example, I get:
?- preOrder(node(1, node(2, node(3,nil,nil), node(4,nil,nil)), node(5,nil,nil)), L).
L = [1,2,3]
When it should be L=[1,2,3,4,5].
Why is it stopping short?
Look at the answers Prolog produces. It's not a single one:
?- preOrder(node(1,node(2,node(3,nil,nil),node(4,nil,nil)),node(5,nil,nil)),L).
L = [1,2,3]
; L = [1,2,3]
; L = [1,2,3]
; L = [1,2,4]
; L = [1,2,4]
; L = [1,2,4]
; L = [1,5]
; L = [1,5]
; L = [1,5]
; false.
Each of your rules describes some part independently of the others. But you need to describe them all together.
The best way to solve this is to use DCGs
It is stopping short because you have two recursive clauses, each one goes just to one side of the tree.
You also have two base cases although the second one is not needed.
So you'd remove the second clause and join the two recursive clauses in only one clause which appends the results from both branches.
E.g.:
preOrder(nil, []).
preOrder(node(X, L, R), [X|T]) :-
preOrder(L, LT),
append(LT, RT, T),
preOrder(R, RT).
You can also use an accumulator for the traversal:
preOrder(Tree, List):-
preOrder(Tree, [], RList),
reverse(RList, List).
preOrder(nil, List, List).
preOrder(node(X, L, R), List, NList) :-
preOrder(L, [X|List], MList),
preOrder(R, MList, NList).
Note that as one commenter said, these definitions for preOrder no not work right to generate trees given a traversal.
You may want to use DCGs to define a procedure that will be reversible, internally using open lists:
preOrder(nil)-->[].
preOrder(node(X, L, R))-->[X], preOrder(L), preOrder(R).
And you would call it using phrase/2:
?- phrase(preOrder(node(1, node(2, node(3,nil,nil), node(4,nil,nil)), node(5,nil,nil))), L).
L = [1, 2, 3, 4, 5].
How to replace a matched node in a binary tree with prolog? Properties of tree: it is not a binary search tree, but every element is unique, so replace operation will effect one element in tree at maximum.
initial tree definition:
tree('Q',
tree('P',
tree('R',
empty,
empty),
tree('S',
empty,
empty)),
tree('T',
empty,
empty))
Let's say new node to replace node 'R' with tree('new', tree('child1', empty, empty), tree('child2', empty, empty))
expected result:
tree('Q',
tree('P',
tree(tree('new',
tree('child1',
empty,
empty),
tree('child2',
empty,
empty)),
tree('S',
empty,
empty)
)),
tree('T',
empty,
empty))
Current status of the code:
:- dynamic([tree/1]).
run:-
retractall(tree(_)),
assertz(tree(tree('Q', tree('P', tree('R', empty, empty), tree('S', empty, empty)), tree('T', empty, empty)))),
retract(tree(T)),
insert('newElement', T, NewTree),
assertz(tree(NewTree)),
tree(T),write(T),!.
insert(NewItem,empty,tree(NewItem,empty,empty)):- !.
insert(NewItem,tree(Element,Left,Right),tree(Element,NewLeft,Right)):-
true, %match function needs to be here
!,insert(NewItem,Left,NewLeft).
insert(NewItem,tree(Element,Left,Right),tree(Element,Left,NewRight)):-
insert(NewItem,Right,NewRight).
I've changed a bit the syntax of your tree, since it was too much verbose for my taste.
Maybe you could consider using a supported tree format, like XML (coded as element/3),
that would give you much power in pattern matching, via library(xpath). Anyway
replace_tree(Old, New, Old, New).
replace_tree(Old, New, t(Key, L, R), t(Key, L1, R1)) :-
replace_tree(Old, New, L, L1),
replace_tree(Old, New, R, R1).
% base case of the recursive data structure
replace_tree(_Old, _New, t, t).
yields
?- T=t(1, t(2, t(3, t(4, t, t), t(5, t, t)), t(6, t, t(3,t,t))),t), replace_tree(t(3,X,Y),t(new,X,Y),T,O).
T = t(1, t(2, t(3, t(4, t, t), t(5, t, t)), t(6, t, t(3, t, t))), t),
X = t(4, t, t),
Y = t(5, t, t),
O = t(1, t(2, t(new, t(4, t, t), t(5, t, t)), t(6, t, t(3, t, t))), t) ;
T = t(1, t(2, t(3, t(4, t, t), t(5, t, t)), t(6, t, t(3, t, t))), t),
X = Y, Y = t,
O = t(1, t(2, t(3, t(4, t, t), t(5, t, t)), t(6, t, t(new, t, t))), t) ;
T = O, O = t(1, t(2, t(3, t(4, t, t), t(5, t, t)), t(6, t, t(3, t, t))), t) ;
false.
Saving to a file and reading from a file.... If you have a Prolog term, you can read it from a file and write it to a file using ISO Prolog read_term and write_term.
In a file 't.txt' you can have:
tree(b, tree(a, empty, empty), tree(c, empty, empty)).
and then, from the top level:
?- open('t.txt', read, File), read_term(File, Tree, []), close(File).
File = <stream>(0x1a38450),
Tree = tree(b, tree(a, empty, empty), tree(c, empty, empty)).
So this is all in the manual of your Prolog implementation. I am using SWI-Prolog for demonstrating.
Then, is your tree organized in any particular way? It doesn't say, but assuming it is a binary search tree, in file tree.pl:
% insert(T0, E, T1)
% Adding E to the binary search tree T0 results in T1.
insert(empty, E, tree(E, empty, empty)).
insert(tree(X, Left, Right), E, tree(X, Left1, Right)) :-
E #< X,
insert(Left, E, Left1).
insert(tree(X, Left, Right), E, tree(X, Left, Right1)) :-
E #> X,
insert(Right, E, Right1).
Then,
?- [tree].
true.
?- open('t.txt', read, File),
read_term(File, Tree, []),
insert(Tree, x, T1),
close(File).
File = <stream>(0x1a20e80),
Tree = tree(b, tree(a, empty, empty), tree(c, empty, empty)),
T1 = tree(b, tree(a, empty, empty), tree(c, empty, tree(x, empty, empty))).
... and so on.
I am new to Prolog and when I query
sortedUnion([1,1,1,2,3,4,4,5], [0,1,3,3,6,7], [0,1,2,3,4,5,6,7]).
I get an error
Exception: (7) unite([_G114, _G162, _G201, _G231, _G243], [_G249, _G297, _G336, _G357, _G369], [0, 1, 2, 3, 4, 5, 6, 7]) ?
So I am hoping someone will be able to tell me where my code is mistaken and why it is wrong?
%undup(L, U) holds precisely when U can be obtained from L by eliminating repeating occurrences of the same element
undup([], []).
undup([X|Xs], [_|B]) :- remove(X,Xs,K), undup(K, B).
remove(_,[],[]).
remove(Y,[Y|T],D) :- remove(Y,T,D).
remove(Y,[S|T],[S|R]) :- not(Y = S), remove(Y,T,R).
%sortedUnion(L1,L2,U) holds when U contains exactly one instance of each element
%of L1 and L2
sortedunion([H|T], [S|R], [F|B]) :- undup([H|T], N), undup([S|R], M), unite(N,M,[F|B]).
unite([], [], []).
unite([X], [], [X]).
unite([], [X], [X]).
unite([H|T], [S|R], [X|Xs]) :- S=H, X is S, unite(T, R, Xs).
unite([H|T], [S|R], [X|Xs]) :- H<S, X is H, unite(T, [S|R], Xs).
unite([H|T], [S|R], [X|Xs]) :- S<H, X is S, unite([H|T], R, Xs).
An advice first: try to keep your code as simple as possible. Your code can reduce to this (that surely works)
sortedunion(A, B, S) :-
append(A, B, C),
sort(C, S).
but of course it's instructive to attempt to solve by yourself. Anyway, try to avoid useless complications.
sortedunion(A, B, S) :-
undup(A, N),
undup(B, M),
unite(N, M, S).
it's equivalent to your code, just simpler, because A = [H|T] and so on.
Then test undup/2:
1 ?- undup([1,1,1,2,3,4,4,5],L).
L = [_G2760, _G2808, _G2847, _G2877, _G2889] ;
false.
Clearly, not what you expect. The culprit should that anon var. Indeed, this works:
undup([], []).
undup([X|Xs], [X|B]) :- remove(X,Xs,K), undup(K, B).
2 ?- undup([1,1,1,2,3,4,4,5],L).
L = [1, 2, 3, 4, 5] ;
false.
Now, unite/3. First of all, is/2 is abused. It introduces arithmetic, then plain unification suffices here: X = S.
Then the base cases are hardcoded to work where lists' length differs at most by 1. Again, simpler code should work better:
unite([], [], []).
unite( X, [], X).
unite([], X, X).
...
Also, note the first clause is useless, being already covered by (both) second and third clauses.