0

I known prolog doesn't return values but i need to update the value of some variable and get it in console with this procedure:

max(A,B,C) :- (A>B -> C is A ; C is B).                                                   
maxAltura([],RES).
maxAltura([c(X,Y)|[]],RES) :- max(RES,Y, SUM).
maxAltura([c(X,Y)|R1],RES) :- RES>Y, maxAltura(R1,RES).
maxAltura([c(X,Y)|R1],RES) :- RES<Y, maxAltura(R1,Y).
maxAltura([c(X,Y)|R1],RES) :- RES=:=Y, maxAltura(R1,Y).

It just takes a list of tuples and gives the max value of second element of that tupes.

This is my output

    maxAltura([c(1,8),c(5,0),c(6,4),c(10,0),c(11,10),c(12,0)],0).
   Call: (7) maxAltura([c(1, 8), c(5, 0), c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 0) ? creep
   Call: (8) 0>8 ? creep
   Fail: (8) 0>8 ? creep
   Redo: (7) maxAltura([c(1, 8), c(5, 0), c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 0) ? creep
   Call: (8) 0<8 ? creep
   Exit: (8) 0<8 ? creep
   Call: (8) maxAltura([c(5, 0), c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Call: (9) 8>0 ? creep
   Exit: (9) 8>0 ? creep
   Call: (9) maxAltura([c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Call: (10) 8>4 ? creep
   Exit: (10) 8>4 ? creep
   Call: (10) maxAltura([c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Call: (11) 8>0 ? creep
   Exit: (11) 8>0 ? creep
   Call: (11) maxAltura([c(11, 10), c(12, 0)], 8) ? creep
   Call: (12) 8>10 ? creep
   Fail: (12) 8>10 ? creep
   Redo: (11) maxAltura([c(11, 10), c(12, 0)], 8) ? creep
   Call: (12) 8<10 ? creep
   Exit: (12) 8<10 ? creep
   Call: (12) maxAltura([c(12, 0)], 10) ? creep
   Call: (13) max(10, 0, _G4361) ? creep
   Call: (14) 10>0 ? creep
   Exit: (14) 10>0 ? creep
   Call: (14) _G4359 is 10 ? creep
   Exit: (14) 10 is 10 ? creep
   Exit: (13) max(10, 0, 10) ? creep
   Exit: (12) maxAltura([c(12, 0)], 10) ? creep
   Exit: (11) maxAltura([c(11, 10), c(12, 0)], 8) ? creep
   Exit: (10) maxAltura([c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Exit: (9) maxAltura([c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Exit: (8) maxAltura([c(5, 0), c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 8) ? creep
   Exit: (7) maxAltura([c(1, 8), c(5, 0), c(6, 4), c(10, 0), c(11, 10), c(12, 0)], 0) ? creep
true .

As you can see it keeps 10 as max value, witch it's correct by i need something like MAX=10. Why just gives me true?

mat
  • 40,498
  • 3
  • 51
  • 78
colymore
  • 11,776
  • 13
  • 48
  • 90

2 Answers2

2

You can alter the given solution to work in both directions by using library(clpfd). As long as the list is sufficiently instantiated is/2 and >/2 work fine but as soon as you try to use them with unbound variables you're in trouble. Consider the following example:

   ?- maxAltura(L,M).
L = [c(_A,M)] ? ;
     ERROR at  clause 1 of user:max/3 !!
     INSTANTIATION ERROR- =:=/2: expected bound value

Fortunately, this can be remedied using clpfd. In my original answer I suggested to simply replace is/2 and >/2 in max/3 by #=/2 and #> like so:

:- use_module(library(clpfd)).

max(A,B,C) :- A#>B -> C #= A ; C #= B.

% use @CapelliC's maxAltura/2 here

However, as pointed out by @mat and @false in the comments this yields too specific/incomplete answers. So I recommend you rather define max/3 as suggested in the comments, e.g.:

:- use_module(library(clpfd)).

max(A,B,C) :- max(A,B) #= C.

% use @CapelliC's maxAltura/2 here

The example query you used still works as expected:

   ?- maxAltura([c(1,8),c(5,0),c(6,4),c(10,0),c(11,10),c(12,0)],M).
M = 10 ? ;
no

The above query with both arguments being variables now works as well:

   ?- maxAltura(L,M).
L = [c(_A,M)] ? ;
L = [c(_A,_B),c(_C,_D)],
M#>=_B,
M#=max(_D,_B),
M#>=_D ? ;
L = [c(_A,_B),c(_C,_D),c(_E,_F)],
M#>=_B,
M#=max(_G,_B),
M#>=_G,
_G#>=_D,
_G#>=_F,
_G#=max(_F,_D) ? 
...
tas
  • 8,100
  • 3
  • 14
  • 22
  • 1
    You now get answers also in the most general case, which is very good! They are currently still too specific though: You lose solutions in the most general case due to impurely committing. A good declarative solution is to write `max(A, B, C) :- max(A, B) #= C.`. This works correctly also in the most general case. – mat May 07 '16 at 06:25
  • 1
    Instantiation errors are not necessarily a bad thing: In the case of @CapelliC's answer they protect the if-then-else from producing incomplete answers. However, once you step above simple arithmetics you can no longer use the primitive if-then-else. See [this](http://stackoverflow.com/a/37057721/772868) for more. – false May 07 '16 at 13:13
  • 1
    If you really insist on using if-then-else, rather write: `max(A,B,C) :- if_(A#>B, C #= A,C #= B).` With `#>(A,B,true) :- A #> B. #>(A,B,false) :- A #=< B`. This may or may not be preferable to @mat's answer. I'd say, most of the time @mat's answer is better. But **both** are correct. – false May 07 '16 at 13:15
  • `max(A,2,2).` fails in your first version. Under no circumstance is this remedial compared to the instantiation error by @CapelliC's solution. – false May 07 '16 at 18:24
  • 1
    Yes, you are right. And I edited my answer accordingly to make clear that I no longer recommend the first version but the second, with max/3 as sugessted by @mat. However, I left the first version in my answer since the comment is referring to it. Marked as my original answer that I no longer recommend. Why is this a problem? – tas May 07 '16 at 19:00
1

this simplified version binds the maximum as last argument.

max(A,B,C) :- A>B -> C is A ; C is B.

maxAltura([c(_,Y)],Y).
maxAltura([c(_,Y)|R1],RES) :- maxAltura(R1,T), max(T,Y,RES).

Note that max/3 is useless: you can write

maxAltura([c(_,Y)|R1],RES) :- maxAltura(R1,T), RES is max(T,Y).

Also, with library(aggregate), you can simplify further:

maxAltura(L,MaxY) :- aggregate(max(Y), X^member(c(X,Y),L), MaxY).
CapelliC
  • 59,646
  • 5
  • 47
  • 90