Prolog: how to write a helper that converts a result into a list?

647 Views Asked by At

Let's assume I have something like:

% person(ID, Name, Age)
person(_, paul, Age).
person(_, Name, 20).
...

...and instead of the typical binding I would like to have a helper function get_person_list(ID, Name, Age) that calls person but returns me a list:

?- get_person_list(_, Name, 20).
[frank, jack, jane].

...to get a list for e.g. all persons with age 20. How can I achieve this?

3

There are 3 best solutions below

4
On

In your code, you don't provide a parameter for the resulting list.

Just call:

findall(Id, person(Id, _, 20), L).

You can make a separate predicate for this query if you really need it often, of course.

get_person_list(Id, List) :- 
  findall(Id, person(Id, _, 20), List).

See also bagof/3, as explained in Boris's answer.

2
On

You could simply write: findall(X,person(_,X,20),L). This will return you a list L which is the list you asked...

Example:

person(_, name1, 20).
person(_, name2, 20).
person(_, name3, 20).
person(_, name4, 20).
person(_, name5, 20).

?- findall(X,person(_,X,20),L).
L = [name1, name2, name3, name4, name5].
0
On

Do not use findall/3 for this. This is the perfect example of how bagof/3 and setof/3 are different from findall/3.

Your example is a bit strange, so here is another database. I removed the first argument because you don't seem to be using it, and added more rows so that there can be more different ages. Note that it makes no sense to have free variables in a table of facts like person, so all rows have values at both positions.

person(abel, 20).
person(bill, 30).
person(clive, 20).
person(diana, 10).
person(evan, 200).
person(fia, 20).

people_age(Age, Ps) :-
        bagof(P, person(P, Age), Ps).

With this definition of people_age/2, you can query, for example:

Who are the people aged 20?

?- people_age(20, Ps).
Ps = [abel, clive, fia].

Group people by their age.

?- people_age(Age, Ps).
Age = 10,
Ps = [diana] ;
Age = 20,
Ps = [abel, clive, fia] ;
Age = 30,
Ps = [bill] ;
Age = 200,
Ps = [evan].

Who is at least 30 years old?

?- people_age(Age, Ps), Age >= 30.
Age = 30,
Ps = [bill] ;
Age = 200,
Ps = [evan].

The last one could be done differently, if you don't want to group by age. Here is how:

?- bagof(P, Age^( person(P, Age), Age >= 30 ), Ps).
Ps = [bill, evan].

or maybe

?- bagof(Age-P, Age^( person(P, Age), Age >= 30 ), Ps).
Ps = [30-bill, 200-evan].

... if you don't want to throw away the age completely.

But you should just read up on "collecting all solutions in Prolog" and the intended use of all three: findall/3, bagof/3, setof/3.