1

I can check if X is a ancestor of Y and can count how many descendants X has I don't know how to list all people that has a given number of descendants. N = COUNT does not work when I put it in the rule.

parent(a,b).
parent(a,d).
parent(b,c).
parent(c,e).

ancestor(X, Y) :- parent(X, Y).
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).

?- aggregate_all(count, ancestor(a,_), COUNT). % output: COUNT = 4
?- aggregate_all(count, ancestor(b,_), COUNT). % output: COUNT = 2
?- aggregate_all(count, ancestor(c,_), COUNT). % output: COUNT = 1
?- aggregate_all(count, ancestor(d,_), COUNT). % output: COUNT = 0
?- aggregate_all(count, ancestor(e,_), COUNT). % output: COUNT = 0

% List all X that have N descendants
list_people(X,N) :- 
Louis Tran
  • 1,154
  • 1
  • 26
  • 46

2 Answers2

1

Yes, you have to write a more complex predicate just like the one you started. One working solution would be the following, although it requires some knowledge on the atoms it is supposed to find. That's why I added the data([a,b,c,d,e]) fact.

data([a,b,c,d,e]).

list_people(Res, N) :- 
    data(X),
    aux(Res, X, N).

aux([], [], _).
aux([H|Res], [H|T], N) :-
    findall(_, ancestor(H,_), List),
    length(List, Count),
    Count = N, 
    !,
    aux(Res, T, N).
aux(Res, [_|T], N) :-
    aux(Res, T, N).
Marco
  • 50
  • 5
  • You can also play around with the `^` operator such as [here](https://stackoverflow.com/a/5930414/13467614). i.e.: `?- aggregate(count, X^ancestor(Y,X), 1).` – Marco Jun 08 '21 at 21:22
0

An alternative solution that also doesn't require an "aggregates" library:

descendants(Person, N, Descendants) :-
    setof(Descendant, ancestor(Person, Descendant), Descendants),
    length(Descendants, N).

What makes this solution interesting is that the setof/3 predicate enumerates solutions for each binding of the Person variable.

Sample call:

| ?- descendants(Person, N, Descendants).

Descendants = [b,c,d,e]
N = 4
Person = a ? ;

Descendants = [c,e]
N = 2
Person = b ? ;

Descendants = [e]
N = 1
Person = c

(1 ms) yes

Note that, being setof/3 based, the descendants/3 predicate fails for all persons with no descendants. We can now use the findall/3 predicate to answer your question. For example, all persons with 4 descendants:

| ?- findall(Person, descendants(Person,4,_), Persons).

Persons = [a]

yes

How about getting a list of persons with no descendants? Assuming a person/1 predicate listing all persons (in Marco's answer a data/1 predicate is used instead):

| ?- findall(Person, (person(Person), \+ descendants(Person,_,_)), Persons).

Persons = [d,e]

yes
Paulo Moura
  • 18,373
  • 3
  • 23
  • 33