Prolog. Comparing elements in a list

Viewed 90

I have a list of people that contains list of people that contains name+age.

person(bob, 20).
person(charlie, 15).
person(steve, 21).
person(david, 25).

I want to create a predicate oldest(List, Name) that will take a list of names and return a name of the oldest person in this list. Something like this:

?- oldest([bob,charlie,steve,david], Name).
Name = david.

?- oldest([bob,charlie], Name).
Name = bob.

As I understand - it should iterate through the list and compare age of everyone in it, but the problem for me is that you should get age first and then return a name.

How can it be done? Thank you in advance.

This is all I have right now:

%If person is older
maximum([X|Y], MaxName, MaxAge):-
    person(X, Age),
    Age > MaxAge,
        %Set MaxName to X, do not really understand how to do it
    maximum(Y,MaxName,Age).
    
%Skipping a younger person, passing current MaxName and MaxValue to the next element
maximum([X|Y], MaxName, MaxAge):-
    person(X, Age),
    Age =< MaxAge,
        maximum(Y,MaxName,MaxAge).
2 Answers

A recursive definition must have at least one clause defining a case base and at least one clause defining a recursive step:

  • For the case base, you can assume that the oldest person in a list containing only one person is that very person.
  • For the recursive step, you can consider that the list contains at least two persons. In this case, you can discard the youngest of the two and proceed with the remainder.
% oldest(++ListOfNames, ?Oldest)

  oldest([Name], Name) :- !.
  oldest([Name1, Name2|Names], Oldest) :-
      person(Name1, Age1),
      person(Name2, Age2),
      (   Age1 > Age2
      ->  oldest([Name1|Names], Oldest)
      ;   oldest([Name2|Names], Oldest) ).

person(bob, 20).
person(charlie, 15).
person(steve, 21).
person(david, 25).

Tests:

?- oldest([bob,charlie,steve,david], Name).
Name = david.

?- oldest([bob,charlie], Name).
Name = bob.

You can use foldl/4 in library(apply):

oldest(People, OldestPerson) :-
    foldl(oldest, People, 0-nobody, _OldestAge-OldestPerson).

oldest(Person, Oldest0-Person0, Oldest2-Person2) :-
    person(Person, Age),
    (   Age > Oldest0
    ->  Oldest2 = Age,
        Person2 = Person
    ;   Oldest2 = Oldest0,
        Person2 = Person0
    ).

The "fold" predicate iterates over a list, passing a value through the iterations. In this case, the value is OldestAge-Person, which contains the oldest age so far and the person with that age.

Note that this predicate doesn't do what you expect if given an empty list ([]). In general, you need to decide what to do with edge cases - for example, you could do:

oldest(People, OldestPerson) :-
    foldl(oldest, People, 0-nobody, OldestAge-OldestPerson),
    OldestAge > 0 .
Related