Determine the maximum depth of a term - prolog

How can I implement a binary predicate ,computes the depth of the first argument as its second argument.
Remark: The depth of variables, numbers, function symbols of arity 0, and predicate symbols of arity 0 is 0.
The depth of a term or an atomic formula is the maximum depth of all subterms or subformulas
plus 1.
?-depth((p(X,a(q(Y)),c), X).
X=3
My effort: i implemented max_list predicate but i could not develop my code more.

This works in one direction I think.
depth(A,0):-
\+compound(A).
depth(A,B):-
compound(A),
A =.. [_H|T],
maplist(depth,T,Depths),
max_list(Depths,Max),
B is Max +1.

Here's a simple straightforward approach. It treats lists as if they are a flat data structure (even through in reality, they are a deeply nested ./2 structure.
depth( T , D ) :- % to compute the depth of an arbitrary term...
depth(T,0,D) % - call the worker predicate with the accumulator seeded to zero.
.
depth( T , CM , MD ) :- var(T) , ! , MD is CM+1 . % an unbound term is atomic : its depth is the current depth + 1 .
depth( T , CM , MD ) :- atomic(T) , ! , MD is CM+1 . % an atomic term is...atomic : its depth is the current depth + 1 .
depth( [X|Xs] , CD , MD ) :- % we're going to treat a list as a flat data structure (it's not really, but conceptually it is)
findall( D , (member(T,[X|Xs),depth(T,0,D)) , Ds ) , % - find the depth of each item in the list
max(Ds,N) , % - find the max depth for a list item.
MD is CD + 1 + N % - the max depth is the current depth + 1 (for the containing list) + the max depth of a list item
. %
depth( T , CD , MD ) :- % for other compound terms...
T \= [_|_] , % - excluding lists,
T =.. [_|Args] , % - decompose it into its functor and a list of arguments
depth(Args,0,N) , % - compute the depth of the argument list
MD is CD + N % - the max depth is the current depth plus the depth of the argument list.
. % Easy!
max( [N|Ns] , M ) :- max( Ns , N , M ) . % to compute the maximum value in a list, just call the worker predicate with the accumulator seeded to zero.
max( [] , M , M ) . % when we hit the end of the list, we know the max depth.
max( [N|Ns] , T , M ) :- % otherwise,
( N > T -> T1 = N ; T1 = T ) , % - update the current high water mark
max(Ns,T1,M) % - recurse down.
. % Easy!

A list is really just a term, with some syntax sugar that eases most common use. So, my depth/2 definition, a 1-liner given compound/1, aggregate/3 and arg/3 availability, answers like:
?- depth(a(a,[1,2,3],c),X).
X = 4.
?- depth(p(X,a(q(Y)),c), X).
X = 3.
edit I will leave you the pleasure to complete it: fill the dots
depth(T, D) :- compound(T) -> aggregate(max(B), P^A^(..., ...), M), D is M+1 ; D = 0.
edit apparently, no pleasure in filling the dots :)
depth(T, D) :-
compound(T)
-> aggregate(max(B+1), P^A^(arg(P, T, A), depth(A, B)), D)
; D = 0.

Related

Prolog rule which replaces with 0 every negative number from a list

I need to write a rule that replaces every negative number from a list with 0. This is my code:
neg_to_0(L,R) :-
(
nth1(X,L,E),
E<0,
replace(E,0,L,L2),
neg_to_0(L2,R2)
) ;
R = L.
replace(_, _, [], []).
replace(O, R, [O|T], [R|T2]) :- replace(O, R, T, T2).
replace(O, R, [H|T], [H|T2]) :- H \= O, replace(O, R, T, T2).
I have a rule "replace" which takes the element that needs to be replaced with 0 and returns the new list, but it stops after the rule replaces the values and return the new list, so i made the function to recall the main function with the new data so it can replace the other negative values :
replace(E,0,L,L2),
neg_to_0(L2,R2)
);
R = L.
On the last iteration, when it could not detect any negative numbers, i made it so that it saves the last correct list, but i only get back a "True" instead of the correct list.
Your code seems... awfully complex.
You seem to be trying to write procedural (imperative) code. Prolog is not an imperative language: one describes "truth" and lets Prolog's "inference engine" figure it out. And, pretty much everything is recursive by nature in Prolog.
So, for your problem, we have just a few simple cases:
The empty list [], in which case, the transformed list is... the empty list.
A non-empty list. [N|Ns] breaks it up into its head (N) and its tail (Ns). If N < 0, we replace it with 0; otherwise we keep it as-is. And then we recurse down on the tail.
To replace negative numbers in a list with zero, you don't need much more than this:
%
% negatives_to_zero/2 replaces negative numbers with 0
%
negatives_to_zero( [] , [] ) . % nothing to do for the empty list
negatives_to_zero( [N|Ns] , [M|Ms] ) :- % for a non-empty list,
M is max(N,0), % get the max of N and 0,
negatives_to_zero(Ns,Ms). % and recurse down on the tail
You can easily generalize this, of course to clamp numbers or lists of numbers, and constrain them to lie within a specified range:
%--------------------------------------------------------------------------------
% clamp( N , Min, Max, R )
%
% Constrain N such that Min <= N <= Max, returning R
%
% Use -inf (negative infinity) to indicate an open lower limit
% Use +inf (infinity) or +inf (positive infinity) to indicate an open upper limit
% -------------------------------------------------------------------------------
clamp( Ns , -inf , +inf , Ns ) .
clamp( N , Min , Max , R ) :- number(N) , clamp_n(N,Min,Max,R).
clamp( Ns , Min , Max , Rs ) :- listish(Ns) , clamp_l(Ns,Min,Max,Rs).
clamp_n( N , _ , _ , R ) :- \+number(N), !, R = N.
clamp_n( N , Min , Max , R ) :- T is max(N,Min), R is min(T,Max).
clamp_l( [] , _ , _ , [] ) .
clamp_l( [X|Xs] , Min , Max , [Y|Ys] ) :- clamp_n(X,Min,Max,Y), clamp(Xs,Min,Max,Ys).
listish( T ) :- var(T), !, fail.
listish( [] ) .
listish( [_|_] ) .

Prolog Ending a Recursion

countdown(0, Y).
countdown(X, Y):-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
So for this program i am trying to make a countdown program which will take Y a number and count down from say 3 to 0 while adding each number to a list so countdown(3, Y). should produce the result Y=[3,2,1]. I can't seem the end the recursion when i run this and i was wondering if anyone could help me?
I cant seem to get this code to work any help? I seem to be getting out of global stack so I dont understand how to end the recursion.
Your original code
countdown( 0 , Y ) .
countdown( X , Y ) :-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
has some problems:
countdown(0,Y). doesn't unify Y with anything.
Y is Y-1 is trying to unify Y with the value of Y-1. In Prolog, variables, once bound to a value, cease to be variable: they become that with which they were unified. So if Y was a numeric value, Y is Y-1 would fail. If Y were a variable, depending on your Prolog implementation, it would either fail or throw an error.
You're never working with lists. You are expecting append(Y,X,Y) to magically produce a list.
A common Prolog idiom is to build lists as you recurse along. The tail of the list is passed along on each recursion and the list itself is incomplete. A complete list is one in which the last item is the atom [], denoting the empty list. While building a list this way, the last item is always a variable and the list won't be complete until the recursion succeeds. So, the simple solution is just to build the list as you recurse down:
countdown( 0 , [] ) . % The special case.
countdown( N , [N|Ns] ) :- % The general case: to count down from N...
N > 0 , % - N must be greater than 0.
N1 is N-1 , % - decrement N
countdown(N1,Ns) % - recurse down, with the original N prepended to the [incomplete] result list.
. % Easy!
You might note that this will succeed for countdown(0,L), producing L = []. You could fix it by changing up the rules a we bit. The special (terminating) case is a little different and the general case enforces a lower bound of N > 1 instead of N > 0.
countdown( 1 , [1] ) .
countdown( N , [N|Ns] ) :-
N > 1 ,
N1 is N-1 ,
countdown(N1,Ns)
.
If you really wanted to use append/3, you could. It introduces another common Prolog idiom: the concept of a helper predicate that carries state and does all the work. It is common for the helper predicate to have the same name as the "public" predicate, with a higher arity. Something like this:
countdown(N,L) :- % to count down from N to 1...
N > 0 , % - N must first be greater than 0,
countdown(N,[],L) % - then, we just invoke the helper with its accumulator seeded as the empty list
. % Easy!
Here, countdown/2 is our "public predicate. It calls countdown/3 to do the work. The additional argument carries the required state. That helper will look like something like this:
countdown( 0 , L , L ) . % once the countdown is complete, unify the accumulator with the result list
countdown( N , T , L ) . % otherwise...
N > 0 , % - if N is greater than 0
N1 is N-1 , % - decrement N
append(T,[N],T1) , % - append N to the accumulator (note that append/3 requires lists)
countdown(N1,T1,L) % - and recurse down.
. %
You might notice that using append/3 like this means that it iterates over the accumulator on each invocation, thus giving you O(N2) performance rather than the desired O(N) performance.
One way to avoid this is to just build the list in reverse order and reverse that at the very end. This requires just a single extra pass over the list, meaning you get O(2N) performance rather than O(N2) performance. That gives you this helper:
countdown( 0 , T , L ) :- % once the countdown is complete,
reverse(T,L) % reverse the accumulator and unify it with the result list
. %
countdown( N , T , L ) :- % otherwise...
N > 0 , % - if N is greater than 0
N1 is N-1 , % - decrement N
append(T,[N],T1) , % - append N to the accumulator (note that append/3 requires lists)
countdown(N1,T1,L) % - and recurse down.
. %
There are several errors in your code:
first clause does not unify Y.
second clause uses append with first and third argument Y, which would only succeed if X=[].
in that clause you are trying to unify Y with another value which will always fail.
Y should be a list (according to your comment) in the head but you are using it to unify an integer.
You might do it this way:
countdown(X, L):-
findall(Y, between(1, X, Y), R),
reverse(R, L).
between/3 will give you every number from 1 to X (backtracking). Therefore findall/3 can collect all the numbers. This will give you ascending order so we reverse/2 it to get the descending order.
If you want to code yourself recursively:
countdown(X, [X|Z]):-
X > 1,
Y is X-1,
countdown(Y, Z).
countdown(1, [1]).
Base case (clause 2) states that number 1 yields a list with item 1.
Recursive clause (first clause) states that if X is greater than 1 then the output list should contain X appended with the result from the recursive call.

Prolog deep version predicate of adding to a list

I have to write a deep version of a predicate that adds a number to each number element in a list and I've done the non-deep version:
addnum(N,T,Y)
this gives something like:
e.g. ?-addnum(7,[n,3,1,g,2],X).
X=[n,10,8,g,9]
but I want to create a deep version of addnum now which should do this:
e.g. ?-addnumdeep(7,[n,[[3]],q,4,c(5),66],C).
X=[n,[[10]],q,11,c(5),73]
Can someone give me some advice? I have started with this:
islist([]).
islist([A|B]) :- islist(B).
addnumdeep(C,[],[]).
addnumdeep(C,[Y|Z],[G|M]):-islist(Z),addnum(C,Y,[G,M]),addnumdeep(C,Z,M).
but I don't think my logic is right. I was thinking along the lines of checking if the tail is a list then runing addnum on the head and then runnig addnumdeep on the rest of the tail which is a list?
maybe you could 'catch' the list in first place, adding as first clause
addnum(N,[T|Ts],[Y|Ys]) :- addnum(N,T,Y),addnum(N,Ts,Ys).
This is one solution. The cut is necessary, or else it would backtrack and give false solutions later on. I had tried to use the old addnum predicate, but you can't know if you have to go deeper afterwards, so it would only be feasible if you have a addnum_3levels_deep predicate and even then it would be clearer to use this solution and count the depth.
addnumdeep(N,[X|Y],[G|H]):-
!, % cut if it is a nonempty list
(number(X)->
G is N + X;
addnumdeep(N,X,G)), % recurse into head
addnumdeep(N,Y,H). % recurse into tail
addnumdeep(_,A,A).
Note that this also allows addnumdeep(7,3,3). if you want it to be addnumdeep(7.3.10), you'll have to extract the condition in the brackets:
addnumdeep(N,[X|Y],[G|H]):-
!, % cut if it is a nonempty list
addnumdeep(N,X,G),
addnumdeep(N,Y,H).
addnumdeep(N,X,Y):-
number(X),!, % cut if it is a number.
Y is N+X.
addnumdeep(_,A,A).
This solution is nicer, because it highlights the three basic cases you might encounter:
It is either a list, then recourse, or a number, for everything else, just put it into the result list's tail (this also handles the empty list case). On the other hand you'll need red cuts for this solution, so it might be frowned upon by some purists.
If you don't want red cuts, you can replace the last clause with
addnumdeep(_,A,A):- !, \+ number(A), \+ A = [_|_].
If you don't want non-lists to be allowed, you could check with is_list if it is a list first and then call the proposed predicate.
I'd start with something that tells me whether a term is list-like or not, something along these lines:
is_list_like( X ) :- var(X) , ! , fail .
is_list_like( [] ) .
is_list_like( [_|_] ) .
Then it's just adding another case to your existing predicate, something like this:
add_num( _ , [] , [] ) . % empty list? all done!
add_num( N , [X|Xs] , [Y|Ys] ) :- % otherwise...
number(X) , % - X is numeric?
Y is X + N , % - increment X and add to result list
add_num( N , Xs , Ys ) % - recurse down
. %
add_num( N , [X|Xs] , [Y|Ys] ) :- % otherwise...
is_list_like( X ) , % - X seems to be a list?
! ,
add_num( N , X , Y ) , % - recurse down on the sublist
add_num( N , Xs , Ys ) % - then recurse down on the remainder
. %
add_num( N , [X|XS] , [Y|Ys] ) :- % otherwise (X is unbound, non-numeric and non-listlike
X = Y , % - add to result list
add_num( N , Xs , Ys ) % - recurse down
. %

Prolog - implementing formula of area

I want to calculate the area of a polygon in Prolog.
The area of a polygon is given by the formula:
| {(x1*y2-y1*x2) + (x2*y3-y2*x3) + .... + (xn*y1 - yn*x1)}/2 |
where (x1,y1),(x2,y2)....(xn,yn) are the points of the polygon.
this is my attempt to implement the formula in Prolog:
area(Points,Area):-areaAcc(Points,0,Area).
areaAcc([(X1,Y1),(X2,Y2)|List], Acc, Area):-
areaAcc2([(X1,Y1),(X2,Y2)|List], Acc, Area, (X1,Y1)).
areaAcc2([(X1,Y1),(X2,Y2)|List],Acc,Area,(FirstX,FirstY)):-
NewAcc is (X1*Y2-Y1*X2) + Acc,
areaAcc2([(X2,Y2)|List], NewAcc, Area, (FirstX,FirstY)).
areaAcc2([(Xm,Ym),(Xn,Yn)|[]],Acc,Area,(FirstX,FirstY)):-
Area is abs((Acc + (Xn*FirstY - Yn*FirstX))/2).
but when I run :
area([(4,10),(9,7),(11,2),(2,2),(4,10)],Area).
I get Area = 51.5 instead of the right answer: Area = 45.5.
can anyone recognize my mistake?
If you simplify the base case (the last clause), and pass a not closed polygon, the result is correct
areaAcc2([(Xn,Yn)],Acc,Area,(FirstX,FirstY)):-
Area is abs((Acc + (Xn*FirstY - Yn*FirstX))/2).
yields
?- area([(4,10),(9,7),(11,2),(2,2)], A).
A = 45.5
Otherwise, implementing with library support it's easier:
area_poly(Points, A) :-
aggregate_all(sum(E),
(append(_, [(X1,Y1),(X2,Y2)|_], Points), E is (X1*Y2-Y1*X2)), A2),
A is abs(A2/2).
Note that is misses the last sum of the formula, yet it yields the correct value, because we pass an already closed polygon...
?- area_poly([(4,10),(9,7),(11,2),(2,2),(4,10)], A).
A = 45.5.
edit aggregate_all/3 is available in SWI-Prolog and YAP (shared source code, I think), and - from Internet search - on SICStus Prolog. I think all these derived from Quintus Prolog. About implementation, I sketched lag, but of course reusing library code is better...
It's been a while since I sweated over this sort of problem. But working from
http://mathworld.wolfram.com/PolygonArea.html
http://www.artofproblemsolving.com/Wiki/index.php/Shoelace_Theorem
http://www.mathopenref.com/coordpolygonarea2.html
This is my take on a solution (assuming that the points are properly ordered):
area_polygon( [] , _ ) :- ! , fail . % not a polygon.
area_polygon( [P1] , _ ) :- ! , fail . % 1 single point is not a polygon.
area_polygon( [P1,P2] , _ ) :- ! , fail . % 2 points is a line segment and not a polygon.
area_polygon( [P1,P2,P3|Ps] , A ) :- % now we're getting somehere: 3 points is a triangle.
append( [P1,P2,P3|Ps] , [P1] , X ) , % append the first point to the list so it ends where it started.
area_polygon( X , 0.0 , A ) % call the worker predicate, seeding its accumulator with 0.0
.
area_polygon( [] , T , A ) :- A is abs( T ) / 2.0 .
area_polygon( [(X1,Y1),(X2,Y2)|Ps] , T , A ) :-
T1 is T + (X1+X2) * (Y2-Y1) ,
area_polygon( [(X2,Y2)|Ps] , T1 , A )
.
You might not need the call to append/3 depending on how you represent the polygon.

Out of local stack trying to test Fibonacci function in Prolog

I made two different Fibonacci functions, the first one worked perfectly. Then I tried to simplify it in an intuitive way. I thought it would work but for some reason it says ERROR: Out of local stack every time I test it.
Working version:
fibonacci(0,0).
fibonacci(1,1).
fibonacci(N,F) :- N1 is N-1, N2 is N-2, fibonacci(N1,F1), fibonacci(N2,F2), F is F1+F2.
Not working version:
fibonacci(0,0).
fibonacci(1,1).
fibonacci(N,F) :- fibonacci(N-1,F1), fibonacci(N-2,F2), F is F1+F2.
Could someone explain me what is the problem with the second one? Thanks.
Your problem is that in your second one you are recursively calling fibonacci/2 with the term N-1 instead of an integer whose value is N-1.
So, for example if you where calling fibonacci(3, F) it would enter in the third clause and call fibonacci(3-1, F1) instead of fibonacci(2, F1). It would then enter again in the third clause and call fibonacci(3-1-1, F1) and so on.
Note that Prolog uses special operator is to perform arithmetic operations.
The first example is right.
Wouldn't this be simpler? Define fibonnaci\3 such that the first two arguments are the two 'seed' elements (normally 1 and 1, though they can be any two positive integers). The third argument is the computed value of that element of the series.
All you have to do is maintain a sliding window as you recurse along the fibonnaci sequence, thus:
%
% the public interface predicate
%
fibonnaci( A , _ , A ) . % 1. return the first element of the series
fibonnaci( _ , B , B ) . % 2. return the second element of the series
fibonnaci( A , B , C ) :- % 3. all subsequent values are the sum of
fib_body( A , B , C ) . % the preceding two elements in the series.
%
% the 'private' worker predicate
%
fib_body( A , B , X ) :- % 1. first, compute the next element of the series
X is A + B . % as the sum of the preceding two elements.
fib_body( A , B , X ) :- % 2. on backtracking, shift our window
C is A + B , % and recurse down to get the next element
fib_body( B , C , X ) . % in the series.

Resources