listprologprolog-setof

setof creates many list instead of one list prolog


I have a function I created using prolog and for some reason it always creating multi list for each element instead of one list, can someone please help me with that?

here is what I wrote:(problem is the last functin creates many lists)

father(_father,_child) :- parent(_father,_child), gender(_father,male).
mother(_mother,_child) :- parent(_mother,_child), gender(_mother,female).

couple(_woman,_man):- gender(_woman,female),gender(_man,male),parent(_man,_child),parent(_woman,_child).

parents(_woman,_man,_child) :- father(_man,_child),mother(_woman,_child).
count([],0).
count([H|T],N) :- count(T,N1) , N is N1+1.

child_to_couple(_woman,_man,_num):- couple(_woman,_man),findall(_child,parents(_woman,_man,_child),_childs),count(_childs,_num).

num_of_childs(_list):- couple(_woman,_man),setof(childrens(_man,_woman,_num),child_to_couple(_woman,_man,_num),_list).

example of data:

gender(sagi,male).
gender(limor,female).
gender(yuval,male).
gender(gilad,male).
gender(shahaf,male).
gender(yaara,female).
parent(eyal,noam).
parent(shiri,yuval2).
parent(eyal,yuval2).
parent(shiri,yonatan).
parent(eyal,yonatan).
parent(shahaf,gan).
parent(yaara,gan).

but when I run

 ?- num_of_childs(_x).

I get:

_x = [childrens(mordechai, miriam, 1)] ;
_x = [childrens(salax, naima, 1)] ;
_x = [childrens(eli, bella, 2)] ;
_x = [childrens(eli, bella, 2)] ;
_x = [childrens(zvi, tova, 1)] ;
_x = [childrens(avram, yokeved, 1)] ;
_x = [childrens(haim, irit, 3)] ;
_x = [childrens(haim, irit, 3)] ;
_x = [childrens(haim, irit, 3)] ;
_x = [childrens(guy, pelit, 2)] ;
_x = [childrens(guy, pelit, 2)] ;
_x = [childrens(eyal, shiri, 3)] ;
_x = [childrens(eyal, shiri, 3)] ;
_x = [childrens(eyal, shiri, 3)] ;
_x = [childrens(sagi, limor, 2)] ;
_x = [childrens(sagi, limor, 2)] ;
_x = [childrens(shahaf, yaara, 1)] ;

instead of:

_x = [childrens(sagi, limor, 2),childrens(sagi, limor, 2),childrens(shahaf, yaara, 1),..........etc]

Solution

  • Your num_of_childs/1 calls couple/2 before the setof/3, so you get the number of results couple/2 returns. Since child_to_couple/3 also calls couple/2 you do not in fact need it here at all.

    num_of_childs(L) :- findall(childrens(M,W,N),child_to_couple(W,M,N),L).
    

    But the big problem is that couple/2, the way you have written it, always succeeds once per child. This propagates up so that child_to_couple/2 and num_of_childs/1 also succeed multiple times.

    If you change to this

    couple(W,M):-
     gender(W,female), gender(M,male),
     ( parent(M,C), parent(W,C) -> true ; false ).
    

    You get only one result per couple, regardless of the number of children. I have the feeling there may be an even simpler way to accomplish this, but I wasn't able to find it.

    ?- num_of_childs(L).
    L = [childrens(eyal,shiri,2),childrens(shahaf,yaara,1)] ? ;
    no
    

    Addition: using a cut would be slightly simpler but also uglier.