Search code examples
prolog

Finding the maximum number between three numbers using 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.


Solution

  • 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