prologprolog-setof

How can I get facts from my knowledge base into a list?


Say I have these facts:

person(fred).
person(jim).
person(mary).

is_person(person(_)).

I would like to get a list like:

[person(fred), person(jim), person(mary)]

but my query with findall/3 does not give the expected result:

?- findall(Person,is_person(Person),ListOfPeople).
ListOfPeople = [person(_5034)].

Similarly with bagof/3:

?- bagof(Person,is_person(Person),ListOfPeople).
ListOfPeople = [person(_5940)].

I do not understand why findall/3 and bagof/3 behave like this.


Solution

  • The correct way:

    findall(person(Person),person(Person),ListOfPeople).
    

    or

    bagof(person(Person),person(Person),ListOfPeople).
    

    Why doesn't your approach work? Consider

    findall(Person,is_person(Person),ListOfPeople).
    

    Prolog tries to fulfill is_person(Person).

    There is a fact is_person(person(_)).

    So, for Person = person(_), we are good! So person(_) will be in the list.

    And that's all, there are no other ways to derive is_person(Person).

    To collect all the Person, we really need to ask for the Person which fulfills person(Person).

    Thus:

    findall(person(Person),person(Person),ListOfPeople).
    

    Prolog will find three Person which fulfill person(Person). As the result should not be a list of Person but of person(Person) we slap a person/1 around Person in the 1st parameter, the template.

    Alternatively (but a bit pointlessly), you could:

    is_person(person(X)) :- person(X).
    
    ?- findall(X,is_person(X),ListOfPeople).
    

    Here, Prolog collects all the X for which is_person(person(X)), which are all the X which appear in a (fact) person(X). Thus X is for example fred. We slap a person/1 around fred in the head of is_person/1. Done.