I'm just starting to work with Prolog, so I don't really understand it. I have the facts:
circle(сhess, abbot).
circle(сhess, hannigan).
circle(сhess, abrams).
circle(crystal_voice, blake).
circle(crystal_voice, weller).
circle(crystal_voice, huxley).
circle(local_studies, barnes).
circle(local_studies, haskins).
circle(local_studies, abrams).
circle(local_studies, aberdeen).
circle(art, barnes).
circle(art, abbot).
circle(art, blake).
person(abbot, male, 20).
person(aberdeen, female, 18).
person(weller, male, 22).
person(abrams, female, 25).
person(adams, male, 21).
person(bond, female, 12).
person(haskins, male, 15).
person(blake, female, 20).
person(barnes, male, 20).
person(hannigan, female, 15).
person(huxley, male, 18).
I need to solve the problem: Find a list of people who participate in more than one circle.
I have a code that finds only the number of circles that a person visits.
count(Name, Count):-
findall(1, circle(_, Name), List),
length(List, Count).
The beginner way is:
circles2(Person) :-
circle(Circle1, Person),
dif(Circle1, Circle2),
circle(Circle2, Person).
circles2_people(People) :-
setof(Person, circles2(Person), People).
... which produces the right answer:
?- circles2_people(People).
People = [abbot, abrams, barnes, blake].
... but is a little inefficient, due to circles2
producing duplicates which setof
then removes:
?- bagof(Person, circles2(Person), People).
People = [abbot, abrams, blake, barnes, abrams, barnes, abbot, blake].
So, can use as an optimization:
person_circles2(Person) :-
person(Person, _, _),
once(circles2(Person)).
The above code "loops" through the persons (due to backtracking), and tries at most once for each person to satisfy the circles2
constraint - which does not duplicate the people:
?- bagof(Person, person_circles2(Person), People).
People = [abbot, abrams, blake, barnes].
However, one would still normally use setof
instead of bagof
, with the benefit of this optimization, to also be clear on intention:
?- setof(Person, person_circles2(Person), People).
People = [abbot, abrams, barnes, blake].