2

Basically I have a list of facts like this:

set(x,2).
set(x,7).
set(x,10).
set(x,4).

I need to find the maximum element of this set.

Input: maximum(x, MaxElement)

Output: MaxElement = 10.

Now the idea itself isn't complicated and I saw many examples online myself. The problem is that I need to use the fail predicate.

Here was my idea (which doesn't work):

maximum(Set, Element1):-
    set(Set,Element1),
    set(Set,Element2), 
    Element2 > Element1,
    fail.

maximum(Set, Element) :- set(Set, Element).

The idea here was that the first rule looks for every element which has a bigger element in the set. If there is a bigger element we fail and stop.

Then ideally for the biggest one (10), we would not fail and move on to the next rule which just sees that it is in the set and returns true.

But like this it still goes to the second rule with every number. Also using cut doesn't seem to work.

Any ideas guys?

false
  • 10,264
  • 13
  • 101
  • 209
PadaKatel
  • 177
  • 2
  • 15
  • See also [this question](http://stackoverflow.com/questions/27317069/collect-all-minimum-solutions-from-a-predicate) and the answers to it. –  Nov 01 '16 at 20:53

1 Answers1

4

You could simply use forall/2 predicate to examine every element like:

maximum(Set, Element1):-
    set(Set,Element1),
    forall(set(Set,Y),(Y>Element1->fail;true)).

Now querying:

?-  maximum(x,X).
X = 10 ;
false.
coder
  • 12,832
  • 5
  • 39
  • 53
  • This works, thank you. I think I also understand why. Out of curiosity, is there anyway to make my original idea work as well (if you have any ideas)? – PadaKatel Nov 01 '16 at 18:22
  • I thought about your idea, but I see two problems first using fail in the end will lead your predicate never to succeed and let's say you find a way to succeed then: set(Set,Element1),set(Set,Element2) will not examine every element so I don't see any way. The main idea of my answer was to find a way to examine every element and force it return the max by using fail predicate.. – coder Nov 01 '16 at 18:26
  • 1
    It would be enough to write: `set(Set, Max), \+ ( set(_, Y), Y > Max )`. See also [here](http://stackoverflow.com/a/32881005/1812457). –  Nov 01 '16 at 20:56
  • 1
    And keep in mind that `forall(Condition, Action)` is the same as `\+ ( Condition, \+ Action )`. See [here](http://eu.swi-prolog.org/pldoc/doc_for?object=forall/2). –  Nov 01 '16 at 21:02
  • Thanks for the info Boris ! though he asked specifically to use fail predicate but this is very useful thanks!! – coder Nov 01 '16 at 21:03
  • 1
    Yes, this is why I gave your answer a +1 and just wrote this as a comment. I suspect the expected solution involves the so-called failure-driven loop, instead of the cleaner forall you used. –  Nov 02 '16 at 07:40