Prolog to count all the leaf nodes in a binary tree - prolog

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.

Related

Prolog - finding a common ancestor in a binary tree

Let's say you have a binary search tree:
t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil)))
which is:
73
/ \
31 101
/ / \
5 83 200
/
97
I need to write a predicate subtree(X1,X2,T) that would take 2 values from the tree (X1 and X2) and find the smallest common parent for them, and store its subtree in T.
So for the example above, if I query : subtree(83,200,X).
I should be getting back:
t(101,t(83,nil,t(97,nil,nil)),t(200,nil,nil))
which is:
101
/ \
83 200
/
97
Since 101 is the smallest common value to both of my numbers, I get that subtree back. How could I do that?
Thanks!
Here is my code for this problem. Just call
tree(X),common_ancestor(83,200,X,Y)
You will get your answer in Y.
tree3(X) :- X = t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil))).
% Chech membership in tree:
in(X, t(X, _, _)).
in(X, t(_, L, _)):-
in(X, L).
in(X, t(_, _, R)):-
in(X, R).
% Getting subtree given the value of root
get_subtree(X, t(X, L, R),Res) :- Res = t(X,L,R).
get_subtree(X, t(_, L, _),Res):-
get_subtree(X, L,Res).
get_subtree(X, t(_, _, R),Res):-
get_subtree(X, R,Res).
% least_common_ancestor(N1, N2, Tree, Res) assignes the value of the least common ancestor to Res.
% Note that it's assumed all nodes have different values.
% Base cases: when one value is the parent of the other, then the parent is the LCA:
least_common_ancestor(N1, N2, t(N1, L, R), N1):-
(in(N2, L) ; in(N2, R)), !.
least_common_ancestor(N1, N2, t(N2, L, R), N2):-
(in(N1, L) ; in(N1, R)), !.
% If one is in the left (right) subtree and the other is in the right (left) subtree then the current root is the LCA
least_common_ancestor(N1, N2, t(X, L, R), Res):-
((in(N1, L), in(N2, R)) ; (in(N1, R), in(N2, L))), !,
Res = X.
% Otherwise, recurse to both subtrees:
least_common_ancestor(N1, N2, t(_, L, _), Res):-
least_common_ancestor(N1, N2, L, Res), !.
least_common_ancestor(N1, N2, t(_, _, R), Res):-
least_common_ancestor(N1, N2, R, Res).
% The main function
commonGP(Ka,Kb,T,ST) :-
least_common_ancestor(Ka,Kb,T,Res), get_subtree(Res,T,ST).

Predicate returns true and not some value

I am trying to make a predicate that takes two numbers K, Num (0 when you use the predicate, it changes after each recursion), a list containing numbers from 1 to K and an associative tree with all keys from 1 to K having values 0. When all numbers from 1 to K are found on the list (Num has the numbers found until then) it returns the rest of the list NL and an associative tree where the value of each key is the times each number is found. It should be used like this:
first(3, 0, [1,3,1,3,1,3,3,2,2,1], T, NL, NT)
where T is the tree described above.
Here is my code:
first(K, K, L, T, L, T):- !.
first(_, _, [], _, [], NT) :-
empty_assoc(NT), !.
first(K, Num, [H|L], T, NL, NT) :-
get_assoc(H, T, V),
Newv is V+1,
put_assoc(H, T, Newv, TT),
V=:=0 -> Newnum is Num+1; Newnum is Num,
first(K, Newnum, L, TT, NL, NT).
My problem is than it returns true instead of the values of NL and NT.
The main problem here is operator precedence. If we ask the interpreter to generate a listing., we get:
first(A, A, B, C, B, C) :- !.
first(_, _, [], _, [], A) :-
empty_assoc(A), !.
first(G, E, [A|H], B, I, J) :-
( get_assoc(A, B, C),
D is C+1,
put_assoc(A, B, D, _),
C=:=0
-> F is E+1
; F is E,
first(G, F, H, _, I, J)
).
This learns us that only in case C =:= 0 does not hold, we will make a recursive call to first/6. This is probably not your intention. Since we only make a recursive call in case the condition does not hold, the TT
If we use brackets, like:
first(K, K, L, T, L, T):- !.
first(_, _, [], _, [], NT) :-
empty_assoc(NT), !.
first(K, Num, [H|L], T, NL, NT) :-
get_assoc(H, T, V),
Newv is V+1,
put_assoc(H, T, Newv, TT),
(V=:=0 -> Newnum is Num+1; Newnum is Num),
first(K, Newnum, L, TT, NL, NT).
With that fixed, we obtain an error:
?- first(3, 0, [1,3,1,3,1,3,3,2,2,1], T, NL, NT)
| .
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR: [13] throw(error(instantiation_error,_7048))
ERROR: [9] assoc:get_assoc(1,_7080,_7082) at /usr/lib/swi-prolog/library/assoc.pl:178
ERROR: [8] first(3,0,[1,3|...],_7112,_7114,_7116) at /tmptest.pl:5
ERROR: [7] <user>
This means that we aim to call get_assoc/3, but with a non-constructed associative array. Note that the empty_assoc/1 is not constructed, it is only constructed at the end when the lists are exhausted.
I think the core problem here is that you are doing too much at once. We can make small predicates that each do limited work.
For example, we can generate an associative array that maps all values between 1 and K to 0 with:
gen_assoc(K, A) :-
empty_assoc(E),
gen_assoc(K, E, A).
gen_assoc(0, A, A).
gen_assoc(K, A, C) :-
K > 0,
put_assoc(K, A, 0, B),
K1 is K-1,
gen_assoc(K1, B, C).
So here gen_assoc(3, A) will unify A with an associative array that maps all numbers from 1 to 3 (both inclusive) to 0.
I leave the rest as an exercise.

Separating a list into a list of fixed length sublists

Given a list L, for instance, [1,2,3,4,5,6,7] and a number N, for instance 3, I would like to make a predicate that would separate the elements of L into lists of size N.
So, the result will be: [[1,2,3], [4,5,6], [7]] in our case.
What I have tried:
% List containing the first N elements of given list.
takeN([X|Xs], 0, []) :- !.
takeN([X|Xs], N, [X|Ys]) :- N1 is N-1, takeN(Xs, N1, Ys).
% Given list without the first N elements.
dropN(R, 0, R) :- !.
dropN([X|Xs], N, R) :- N1 is N-1, dropN(Xs, N1, R).
% size of list.
sizeL([], 0) :- !.
sizeL([X|Xs], N) :- sizeL(Xs, N1), N is N1+1.
blockify(R, N, [R|[]]) :- sizeL(R, N1), N1 < N, !.
blockify([X|Xs], N, [Y|Ys]) :- sizeL(R, N1), N1 >= N, takeN([X|Xs], N, Y),
dropN([X|Xs], N, Res), blockify(Res, N, Ys).
It doesn't work (blockify([1,2,3], 2, R) for example returns false, instead of [[1,2], [3]]).
I can't find where I'm mistaken, though. What's wrong with this?
I think you are making thinks a bit overcomplicated. First of all let's exclude the case where we want to blockify/3 the empty list:
blockify([],_,[]).
Now in the case there are elements in the original list, we simply make use of two accumulators:
- some kind of difference list that stores the running sequence; and
- an accumulator that counts down and look whether we reached zero, in which case we append the running difference list and construct a new one.
So this would be something like:
blockify([H|T],N,R) :-
N1 is N-1,
blockify(T,N1,N1,[H|D],D,R).
Now the blockify/5 has some important cases:
we reach the end of the list: in that case we close the difference list and append it to the running R:
blockify([],_,_,D,[],[D]).
we reach the bottom of the current counter, we add the difference list to R and we intialize a new difference list with an updated counter:
blockify([H|T],N,0,D,[],[D|TR]) :-
blockify(T,N,N,[H|D2],D2,TR).
none of these cases, we simply append the element to the running difference decrement the accumulator and continue:
blockify([H|T],N,M,D,[H|D2],TR) :-
M > 0,
M1 is M-1,
blockify(T,N,M1,D,D2,TR).
Or putting it all together:
blockify([],_,[]).
blockify([H|T],N,R) :-
N1 is N-1,
blockify(T,N1,N1,[H|D],D,R).
blockify([],_,_,D,[],[D]).
blockify([H|T],N,0,D,[],[D|TR]) :-
blockify(T,N,N,[H|D2],D2,TR).
blockify([H|T],N,M,D,[H|D2],TR) :-
M > 0,
M1 is M-1,
blockify(T,N,M1,D,D2,TR).
Since in each recursive call all clauses run in O(1) and we do the recursion O(n) deep with n the number of elements in the original list, this program runs in O(n).
if your Prolog provides length/2, a compact solution could be:
blockify(R, N, [B|Bs]) :-
length(B, N),
append(B, T, R),
!, blockify(T, N, Bs).
blockify(R, _N, [R]).
Let me teach you how to debug a Prolog query:
1) blockify([1,2,3], 2, R)
2) does it match blockify(R, N, [R|[]]) ? oh yes,
it can be bound to blockify([1, 2, 3], 2, [[1, 2, 3]])
3) let's evaluate the body: sizeL(R, N1), N1 < N, !.
Replace R and N, we get: sizeL([1, 2, 3], N1), N1 < 2, !.
4) evaluate sizeL([1, 2, 3], N1): for brevity, since it's a common
list count predicate, the result should be obvious: N1 = 3
5) evaluate N1 < N: 3 < 2 => false
6) since the rest are all , (and operator) a single false
is enough to make the whole body to evaluate to false
7) there you go, the predicate is false
See where your mistake is?

Prolog in-order traversal

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.

Traverse a list in Prolog

I have a problem like this: find all elements in a list such that all element(s) immediately besides it is/are odd numbers.
For example
?- find([20,1,2,3,4,5,6,7,8,10], L).
L = [20, 2, 4, 6]
Normally in other languages I would traverse the list and check the condition, but I don't know how to "think" in Prolog in this scenario. How should I approach this?
visit the list considering the pair of head elements:
find([A,B|R], [A|T]) :-
is_odd(B),
... etc etc
You'll need to add obviously the base recursion case and the case when A must be discarded.
EDIT: a better solution based on CapelliCs suggestion (this uses the isodd predicate from below):
% if N0 and N2 are odd, cut, add N1 to the result and recurse
ff([N0,N1,N2|T], [N1|R]) :- isodd(N0), isodd(N2), !, ff([N1,N2|T], R).
% for any other case where the list has at least three members, cut and recurse
ff([_,N1,N2|T], R) :- !, ff([N1,N2|T], R).
% this is reached if the list has less that three members - we're done
ff(_, []).
% append and prepend '1' to the list to deal with the edges, call ff.
find(L, R) :- append(L, [1], L1), ff([1|L], R).
My old solution which keept track of the two previous values with extra arguments:
% isodd(+N)
% helper predicate that succeds for odd numbers.
isodd(N) :- mod(N, 2) =:= 1.
% find(+I, +N1, +N2, +R, -L)
% find/5 is the predicate doing the actual work.
% I is the input list, N1 and N2 are the numbers before the current one,
% R is the intermediate result list and L the result.
% we're done if the input list is empty
find([], _, _, R, R) :- !.
% check if N0 and N2 are odd to see if N1 should be appended to the list.
% if yes, do a cut, append N1 to the result and recurse.
find([N0|T], N1, N2, R, L) :-
isodd(N0), isodd(N2), !,
append(R, [N1], R1), find(T, N0, N1, R1, L).
% if N0 and N2 are not odd (and thus the cut in the previous clause isn't
% reached) just continue the recursion.
find([N0|T], N1, _, R, L) :- find(T, N0, N1, R, L).
% find(+I, -L)
% this predicate is the entry point - initialize the result list and the first
% values for N1 and N2, and append 1 to the input list so we don't need an extra
% predicate for dealing with the last item.
find(I, L) :- append(I, [1], I1), find(I1, 1, 0, [], L).

Resources