Prolog - Cleaner way to define a rule

Viewed 104

Just started out programming a bit in Prolog but I feel like I've either misunderstood some concept or maybe you have to have more rules.

I would like it so it checks that the son is male and has at least one parent (father/mother)

% Son
son(X, Y):- male(X), father(Y, X).
son(X, Y):- male(X), mother(Y, X).

Is there any way to combine this easily so I don't need two rows which are similar? Maybe something like: Check if X is a male, then check if he has a father, if not check if he has a mother. Only return if X is a male and has a father and/or mother.

4 Answers

If you want an explicit disjunction, you can instead write:

son(X, Y) :- male(X), (father(Y, X); mother(Y, X)).

Here I used ;/2 operator. A ; B tries the goal A first and if that does not succeed tries B.

With the following data, we can see the only advantage is that male(X) queried twice:

male(a).
father(b, a).
mother(c, a).

Trace of your original code:

      1    1  Call: son(a,c) ? 
      2    2  Call: male(a) ? 
      2    2  Exit: male(a) ? 
      3    2  Call: father(c,a) ? 
      3    2  Fail: father(c,a) ? 
      2    2  Call: male(a) ?              ; <--- just this is not queried
      2    2  Exit: male(a) ? 
      3    2  Call: mother(c,a) ? 
      3    2  Exit: mother(c,a) ? 
      1    1  Exit: son(a,c) ? 

Trace of the new code using ;/2 disjunction.

      1    1  Call: son(a,c) ? 
      2    2  Call: male(a) ? 
      2    2  Exit: male(a) ? 
      3    2  Call: father(c,a) ? 
      3    2  Fail: father(c,a) ? 
      3    2  Call: mother(c,a) ? 
      3    2  Exit: mother(c,a) ? 
      1    1  Exit: son(a,c) ? 

Another option is to use an auxiliary predicate parent/2 for subsuming father/2 and mother/2:

parent(A, B) :- father(A, B).
parent(A, B) :- mother(A, B).

son(A, B) :- male(A), parent(B, A).

To answer your wording, not the code you wrote, it is

son(S):- male(S), setof( Y, (mother(Y,S) ; father(Y,S)), _).

Like you said, a son is someone who's male and has at least one parent (or both of them).

The difference from the other answers is that the same person isn't reported twice if he has both a father and a mother:

2 ?- [user].
son(S):- male(S), setof( Y, (mother(Y,S) ; father(Y,S)), _).
|: male(a).
|: father(b,a).
|: mother(c,a).
|: 
true.

3 ?- son(X).
X = a.

I would probably do something like this because it better expresses intent:

son( X , Y ) :- male(X), parent(Y,X) .

parent( Y, X ) :- mother(Y,X) .
parent( Y, X ) :- father(Y,X) .
Related