0
votes

I have an assignment where I must create a family tree in prolog. While all my functions work the single_child does not, returning always false. Inside each function we are only allowed to use child_of(CHILD, PARENT) and female(PERSON). This is what I have wrote (I have omitted the other functions as well as people assignments).

:- discontiguous
    child_of/2,
    female/1,
    single_child/1.

single_child(CHILD) :- 
   child_of(CHILD, PARENT), 
   SIBLING \= CHILD, 
   \+ child_of(SIBLING, PARENT).

Also, moving the SIBLING \= CHILD after \+ child_of(SIBLING, PARENT) gives an 'Singleton variable' error. Is my approach wrong?

2

2 Answers

1
votes

Short answer: To express "there is no other child of Parent", i.e., "there is no child of Parent different from Child", you would write:

single_child(Child) :-
    child_of(Child, Parent),
    \+ ( child_of(Sibling, Parent), Sibling \= Child ).

(Not tested because you didn't give us any test cases.)

A key point is that the two goals child_of(Sibling, Parent), Sibling \= Child together express the condition "there is another child of Parent", so you have to negate them together to get the right meaning. My preferred way would be to extract this to a separate predicate meaning "Person has sibling Sibling":

person_sibling(Person, Sibling) :-
    child_of(Person, Parent),
    child_of(Sibling, Parent),
    Person \= Sibling.

And then write single_child as:

single_child(Child) :-
    child_of(Child, _Parent),            % Child is somebody's child
    \+ person_sibling(Child, _Sibling).  % Child has no siblings

Another key point is that you should only use \= when both sides are bound to non-variable terms. In your code Sibling is not bound to anything at the point you reach the \= goal. That is why my code first calls child_of(Sibling, Parent) to give Sibling a concrete value before then comparing that concrete value to Child. Modern Prologs usually have a dif predicate that does not have this restriction: You can write dif(Child, Sibling), child_of(Sibling, Parent) and it will do the right thing even if some operands are unbound variables at that point. If your Prolog has dif, you should use it instead of \=. You can and usually should use dif before all of its operands become bound.

Final note: You shouldn't just blindly make all your predicates discontiguous. It may be useful for facts like child_of or female if you want to mix up the order of those facts. But as a beginner you should never encounter a situation where it makes sense for a rule like single_child to be marked discontiguous. In fact, the annotation does not make any sense for a predicate that consists of a single clause.

0
votes

The discontiguous is not so interesting, but this is:

single_child(CHILD) :- 
   child_of(CHILD, PARENT), 
   SIBLING \= CHILD,               <----- what is SIBLING at this point?
   \+ child_of(SIBLING, PARENT).

You never collect a valid SIBLING from the database.

In fact, the only thing we know about SIBLING is that it is a term that does not unify with CHILD.

That could be anything - but Prolog needs to pick from a finite domain.

Tell it where to find SIBLING.