Finding the maximum number between three numbers using prolog - prolog

I have started learning prolog since yesterday and i am told to find the maximum number between three numbers. I am using SWI Prolog and this is the program i wrote so far.
% If-Elif-Else statement
gte(X,Y,Z) :- X > Y,write('X is greater').
gte(X,Y,Z) :- X =:= Y,write('X and Y are same').
gte(X,Y,Z) :- X < Y < Z,write('Y is greater').
gte(X,Y,Z) :- X < Z,write('Z is greater').
gte(X,Y,Z) :- X > Z,write('X is greater').
gte(X,Y,Z) :- Y > Z,write('Y is greater').
gte(X,Y,Z) :- Y < Z,write('Z is greater').
gte(X,Y,Z) :- X=:=Z,write('X and Z are same').
gte(X,Y,Z) :- Y=:=Z,write('Y and Z are same').
The output should've been ->
gte(12,24,36)
24 is greater.
True
instead its showing me this
Warning: c:/users/i3/documents/prolog/test.pl:3:
Warning: Singleton variables: [Z]
Warning: c:/users/i3/documents/prolog/test.pl:4:
Warning: Singleton variables: [Z]
Warning: c:/users/i3/documents/prolog/test.pl:5:
Warning: Singleton variables: [Z]
Warning: c:/users/i3/documents/prolog/test.pl:6:
Warning: Singleton variables: [Y]
Warning: c:/users/i3/documents/prolog/test.pl:7:
Warning: Singleton variables: [Y]
Warning: c:/users/i3/documents/prolog/test.pl:8:
Warning: Singleton variables: [X]
Warning: c:/users/i3/documents/prolog/test.pl:9:
Warning: Singleton variables: [X]
Warning: c:/users/i3/documents/prolog/test.pl:10:
Warning: Singleton variables: [Y]
Warning: c:/users/i3/documents/prolog/test.pl:11:
Warning: Singleton variables: [X]
true.
I cannot understand where is the error in this program.

A huge chunk of learning Prolog is learning to think recursively. So....
You should first solve the simplest problem: what is the greater of just 2 numbers? That's pretty easy, right?
max( X, X , X ) .
max( X, Y , X ) :- X > Y .
max( X, Y , Y ) :- X < Y .
Now that we can do that, expanding the solution space to consider 3 values is also easy: just determine the max value of the 1st two values, and then, the max of that and the 3rd value:
max( X , Y , Z , R ) :- max(X,Y,T), max(T,Z,R).
Further expanding the solution to consider any number of values is also easy: just repeat the above process until you've winnowed the list down to a single value.
max( [Z] , Z ) . % the max of a 1-item list is that item.
max( [X,Y|Zs] , M ) :- % the max of a list of 2+ items can be found by...
max(X,Y,Z) , % - find the max of the first two items, and
max([Z|Zs],M) % - recursing down, replacing those two items with that max value
. % Easy!
[Edited to note]
The basic algorithm for determining the maximum value of a list of number is:
While the list is of length greater than 1,
Determine the greater of the 1st two elements of the list
[Recursively replace the 1st two elements of the list with the greater of the two values
So on each iteration of the recursion, the length of the source list is reduced by 1 and the head of the list is always the current high-water mark, the current maximum value. Once the length of the list has been reduced to one, we have solved the problem at hand.
One could coalesce max/3 and max/2 into a single predicate:
max( [X] , X ) .
max( [X,Y|Zs] , R ) :- X =:= Y , max( [X|Zs] , R ) .
max( [X,Y|Zs] , R ) :- X > Y , max( [X|Zs] , R ) .
max( [X,Y|Zs] , R ) :- X < Y , max( [Y|Zs] , R ) .
But I think that finding the maximum of 2 values is a common enough special case that it's worth breaking it out into its own predicate: max(X,Y,Max).
One could also use a helper predicate with an extra argument that explicitly carries the extra state, the current high value, thus:
max( [X|Xs] , R ) :- max(Xs,X,R) .
max( [] , R , R ) .
max( [X|Xs] , Y , R ) :- X =:= Y , max(Xs,Y,R) .
max( [X|Xs] , Y , R ) :- X < Y , max(Xs,Y,R) .
max( [X|Xs] , Y , R ) :- X > Y , max(Xs,X,R) .
So in the above, we simply pop off the head of the source list and invoke the helper with its accumulator seeded with that value. Once the source list is exhausted, the accumulator contains the desired result.
TIMTOWTDI[1] as they say.
[1] There's more than one way to do it

Related

Why would a provided prolog function not be allowed to be used twice in the program

error: Full stop in clause-body? Cannot redefine ,/2
I understand that the error is caused when our Prolog system already provides X/2, and you cannot redefine it. so you can either omit your own definition (thus, using the system-provided one instead) or rename your predicate - but im not understanding why I would be getting the error with this code
county_deaths(onedia,45).
county_deaths(essex,0).
county_deaths(franklin,0).
county_deaths(clinton,4).
county_deaths(fulton,19).
county_deaths(hamilton,1).
county_deaths(herkimer,3).
county_deaths(saratoga,14).
county_deaths(stLawrence,2).
county_deaths(warren,27).
county_deaths(washington,14).
county_deaths(lewis,5).
county_cases(clinton,95).
county_cases(essex, 36).
county_cases(franklin, 20).
county_cases(fulton, 196).
county_cases(hamilton, 5).
county_cases(herkimer, 103).
county_cases(lewis, 20).
county_cases(onedia, 917).
county_cases(saratoga, 463).
county_cases(stLawrence, 197).
county_cases(warren, 251).
county_cases(washington, 228).
start :-
findall( N , county_deaths(_,N), [N|Ns] ) ,
min_max( Ns , N , N , Min, Max ) ,
display_max(Max),
display_min(Min)
.
findall( N , county_cases(_,N), [N|Ns] ) ,
min_max( Ns , N , N , Min, Max ) ,
display_maxCase(Max),
display_minCase(Min)
.
min_max( [] , Min , Max , Min , Max ) .
min_max( [N|Ns] , X , Y , Min , Max ) :- N < X , !, min_max(Ns,N,Y,Min,Max) .
min_max( [N|Ns] , X , Y , Min , Max ) :- N > Y , !, min_max(Ns,X,N,Min,Max) .
min_max( [_|Ns] , X , Y , Min , Max ) :- min_max(Ns,X,Y,Min,Max) .
% multiple counties could have the same number of deaths, so
% we use findall/3 to enumerate all counties with the specified
% number of deaths.
display_max(N) :-
findall(C,county_deaths(C,N),Cs) ,
writeln(max_deaths:Cs:N)
.
display_min(N) :-
findall(C,county_deaths(C,N),Cs) ,
writeln(min_deaths:Cs:N)
.
display_maxCase(N) :-
findall(C,county_cases(C,N),Cs) ,
writeln(max_cases:Cs:N)
.
display_minCase(N) :-
findall(C,county_cases(C,N),Cs) ,
writeln(min_cases:Cs:N)
.
I believe the error is with the findall function - but that is something prolog provides and i need to use this method 3 times in my program. I have tried to solve this by renaming the method - but then it is no longer the provided findall function. how can i get around this error?

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( [_|_] ) .

Determine the maximum depth of a term

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.

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

How does the recursing work in prolog for adding number

My aim is to take the numbers between X and Y and produce Z.
num_between(3,6, All)
For example, if X is 3 and Y is 6 then Z is a list of the numbers between X and Y inclusive. Something like num_between(3,6,[3,4,5,6]) should evaluate as true. Here's what I have so far:
num_between(0,0, []).
num_between(X,Y, All) :-
increase(X, New) , % increase number X++
\+(X = Y) , % check if X is not equal to Y
num_between(New,Y,[All|X]) . % requestion ???
increase(F,N) :- N is F+1 .
increase/1 is working and returns number that is required, but
when recursion is gone through num_between/3 it goes unlit: X is 6 then it fails as I want,
but I can not manage to keep numbers or to return them. All = [3,4,5,6].
All = All + F. Could anyone help please.
Your base clause is incorrect: since you never decrease X or Y, they would never get to zero (unless Y starts at zero, and X starts at a non-positive value). The base clause should look like this:
num_between(X, Y, []) :- X > Y.
This ensures that you get an empty result when the user enters an invalid "backward" range (say, from 6 to 3).
Now to the main clause: all you need to do is to check that the range is valid, get the next value, and make a recursive call, like this:
num_between(X, Y, [X|Tail]) :-
X =< Y,
Next is X + 1,
num_between(Next, Y, Tail).
Demo.
Your original code made an error when constructing a list - it tried to use X as the "tail" of the list, which is incorrect:
num_between(New,Y,[All|X]).
you pass on All, the result after an "expansion", down through the recursive chain of invocation. It should be the other way around - you need to pass in a Tail to collect the result, and then pre-pend X to it when the recursive invocation is over.
You have to change both your base case and your recursive clause:
num_between(X, X, [X]).
num_between(X, Y, [X|L]):-
X < Y,
increase(X, New),
num_between(New, Y, L).
First clause is the base case, it states that the number ranging from X and X is just [X].
The recursive clause states that a number X which is less than a number Y should have it in the output list (thus the [X|L] in the third argument of the head), then it increases the value (i'm just using your helper procedure for that) and recursively calling itself now with the New value for the first argument.
I would write this along these lines:
numbers_between( X , X , [X] ) . % if X and Y have converged, we have the empty list
numbers_between( X , Y , [X|Zs] ) :- % otherwise, add X to the result list
X < Y , % - assuming X is less than Y
X1 is X+1 , % - increment X
numbers_between(X1,Y,Zs) % - recurse down
. %
numbers_between( X , Y , [X|Zs] ) :- % otherwise, add X to the result list
X > Y , % - assuming X > Y
X1 is X-1 , % - decrement X
numbers_between(X1,Y,Zs) % - recurse down
. %

Resources