I am just learning prolog and I ran into the problem where I get the same answer twice when asking for niece and nephew. I just cannot wrap my head around it. Does anyone know what I am doing wrong? Perhaps it is something having two rules for each cousin? But it works fine when asking for brother or sister..
UPDATE: I tried out some things, and after returning to the code I wrote down here, the rule of a niece being only a female doesn't work anymore. and the nephew returns false.
So I have the code:
male( ted ).
male( john ).
male( aron ).
male( nico ).
male( gio ).
female( liza ).
female( sophie ).
female( lily ).
female( elin ).
female( daisy ).
parent_of( lily , aron ).
parent_of( john , aron ).
parent_of( john , elin ).
parent_of( lily , elin ).
parent_of( sophie , gio ).
parent_of( sophie , nico ).
parent_of( daisy , liza ).
parent_of( ted , liza ).
father_of(X,Y):- male(X),
parent_of(X,Y).
mother_of(X,Y):- female(X),
parent_of(X,Y).
sister_of( X , Y ) :- % (X,Y or Y,X) %
female(X),
father_of(F,Y),
father_of(F,X),
X \= Y.
sister_of( X , Y ) :-
female(X),
mother_of(M,Y),
mother_of(M,X),
X \= Y.
brother_of(X,Y):- %(X,Y or Y,X)%
male(X),
father_of(F,Y),
father_of(F,X),
X \= Y.
brother_of(X,Y):- male(X),
mother_of(M,Y),
mother_of(M,X),
X \= Y.
niece_of(X,Y):-
dif(X,Y),
female(X),
parent_of(W,Y),
parent_of(P,X),
brother_of(P,W).
niece_of(X,Y) :-
dif(X,Y),female(X),
parent_of(W,Y),
parent_of(P,X),
sister_of(P,W).
nephew_of(X,Y) :-
dif(X,Y),
male(X),
parent_of(W,Y),
parent_of(P,X),
brother_of(P,W).
nephew_of(X,Y):-
dif(X,Y),
male(X),
parent_of(W,Y),
parent_of(D,X),
sister_of(D,W).
The output I get is
?- niece_of(X,liza).
X = aron ;
X = aron ;
X = nico ;
X = nico ;
X = gio ;
X = gio ;
false.
?- nephew_of(X,liza).
false.
First of all, according to your database, there is no one who is anyone's niece or nephew. This can be clearly seen when you draw the graph for the relation parent_of/2.
female(lily).
female(sophie).
female(daisy).
female(elin).
female(liza).
male(john).
male(ted).
male(aron).
male(gio).
male(nico).
parent_of(lily, aron).
parent_of(lily, elin).
parent_of(john, aron).
parent_of(john, elin).
parent_of(sophie, gio).
parent_of(sophie, nico).
parent_of(daisy, liza).
parent_of(ted, liza).
Now, consider a first version of a rule that defines the relation siblings0/2:
siblings0(Person1, Person2) :-
dif(Person1, Person2),
parent_of(Parent, Person1),
parent_of(Parent, Person2).
Using this rule, there are two distinct ways to prove that Aron and Elin are siblings (first, because they both have the same mother; second, because they both have the same father). However, there is only one way to prove that Gio and Nico are siblings (since there is no information on who is the father of each one of them).
?- siblings0(aron, elin).
true ; % <== same mother
true ; % <== same father
false.
?- siblings0(gio, nico).
true. % <== same mother
To avoid redundancy, we can use the the ISO built-in predicate once/1:
siblings(Person1, Person2) :-
dif(Person1, Person2),
person(Person1),
person(Person2),
once( ( parent_of(Parent, Person1),
parent_of(Parent, Person2) ) ).
person(Person) :-
( female(Person)
; male(Person) ).
Now, we get only one answer for each query:
?- siblings(aron, elin).
true.
?- siblings(gio, nico).
true.
Thus, extending your database and using the predicate siblings/2 to define other relations, everything will work fine!
female(lily).
female(sophie).
female(daisy).
female(elin).
female(liza).
female(pat). % <== add
male(john).
male(ted).
male(aron).
male(gio).
male(nico).
male(coy). % <== add
parent_of(lily, aron).
parent_of(lily, elin).
parent_of(john, aron).
parent_of(john, elin).
parent_of(sophie, gio).
parent_of(sophie, nico).
parent_of(daisy, liza).
parent_of(ted, liza).
parent_of(elin, pat ). % <== add
parent_of(elin, coy). % <== add
parent_of(gio, pat). % <== add
parent_of(gio, coy). % <== add
siblings(Person1, Person2) :-
dif(Person1, Person2),
once( ( parent_of(Parent, Person1),
parent_of(Parent, Person2) ) ).
mother_of(Mother, Child):-
female(Mother),
parent_of(Mother, Child).
father_of(Father, Child):-
male(Father),
parent_of(Father, Child).
sister_of(Sister, Person) :-
dif(Sister, Person),
female(Sister),
siblings(Sister, Person).
brother_of(Brother, Person) :-
dif(Brother, Person),
male(Brother),
siblings(Brother, Person).
niece_of(Niece, Person):-
female(Niece),
parent_of(Parent, Niece),
siblings(Parent, Person).
nephew_of(Nephew, Person):-
male(Nephew),
parent_of(Parent, Nephew),
siblings(Parent, Person).
Examples:
?- niece_of(Niece, Person).
Niece = pat,
Person = aron ;
Niece = pat,
Person = nico.
?- nephew_of(Nephew, Person).
Nephew = coy,
Person = aron ;
Nephew = coy,
Person = nico.
First of all some simplification and extended family tree
male(ted).
male(john).
male(aron).
male(nico).
male(gio).
male(ken).
male(vladimir).
female(liza).
female(sophie).
female(lily).
female(elin).
female(daisy).
female(barbie).
parent(lily, aron).
parent(john, aron).
parent(john, elin).
parent(lily, elin).
parent(sophie, gio).
parent(sophie, nico).
parent(vladimir, gio).
parent(vladimir, nico).
parent(daisy, liza).
parent(ted, liza).
% grandfathers
parent(ken, lily).
parent(ken, daisy).
parent(ken, sophie).
parent(barbie, lily).
parent(barbie, daisy).
parent(barbie, sophie).
father(Father, Child) :-
male(Father),
parent(Father, Child).
mother(Mother, Child) :-
female(Mother),
parent(Mother, Child).
sibling(X, Y) :-
father(Father, Y),
father(Father, X),
X\=Y.
sister(Sister, Sibling) :-
female(Sister),
sibling(Sister, Sibling).
brother(Brother, Sibling) :-
male(Brother),
sibling(Brother, Sibling).
niece(Niece, ParentsSibling) :-
female(Niece),
parent(NiecesParent, Niece),
sibling(NiecesParent, ParentsSibling).
nephew(Nephew, ParentsSibling) :-
male(Nephew),
parent(NephewParent, Nephew),
sibling(NephewParent, ParentsSibling).
I've added sibling/2 predicate to remove some reused code.
Why did you have duplications on nephew?
You did use the parent predicate there when trying to define the sister of the nephew's parent, in that case, you would get each sibling twice (once because of evaluation with mother and once because of evaluating with father). You can verify that with trace\0.
Workaround
When looking for siblings I'm checking for 2 people with the same father (check sibling/2 predicate).
If for some reason your relations would be able to have situations that human beings can exist without fathers it won't work. In that case, you would need to modify sibling/2 predicate and use parent\2, produce all values (with findall for example) and then return distinct values.
Related
If you have these facts:
parent(albert, bob).
parent(albert, betsy).
parent(albert, bill).
parent(alice, bob).
parent(alice, betsy).
parent(alice, bill).
parent(bob, carl).
parent(bob, charlie).
And then this code:
grand_parent(X, Y) :-
parent(Z, X),
parent(Y, Z).
How to write a query to find all pairs that share
grand-parenthood relationship?
A basic query would look like this:
?- grand_parent(X,Y).
giving the following output (after one answer ; will show the next one):
X = carl,
Y = albert;
X = carl,
Y = alice;
X = charlie,
Y = albert;
X = charlie,
Y = alice;
false.
However if you want to have all grandchildren of a specific person, you can either programm your own "find_all_grandchilren"-predicate or just use an inbuild predicate for collecting all answers. Example:
?- findall(Y,grand_parent(carl,Y),X).
X = [albert, alice].
Which reads as: find every term Y which satisfies grand_parent(carl,Y) and put it in the list X.
Tested with SWISH.
Getting the data model right is important.
You might find it useful to change your data model up to better represent the real-world model. Something like
parents( carol, bob, jim ).
Which is to say, carol and bob are respectively, jim's mother and father.
Once you have that things fall into place:
mother(M,C) :- parents(M,_,C).
father(F,C) :- parents(_,F,C).
child(P,C) :- mother(P,C).
child(P,C) :- father(P,C).
child(M,F,C) :- parents(M,F,C).
children(P,Cs) :- findall( C , child(P,C), Cs).
children(M,F,Cs) :- findall( C, child(M,F,C), Cs).
family(M,F,[M,F|Cs]) :- children(M,F,Cs).
sibling(S,C) :- parents(M,F,C), parents(M,F,S).
grandparents(GM,GF,C) :- mother(M,C), parents(GM,GF,M).
grandparents(GM,GF,C) :- father(F,C), parents(GM,GF,F).
Now, to get the list of all grandparent pairs is trivial:
all_grandparents(GPs) :- findall( GM:GF, grandparents(GM,GF,_), GPs ).
I'm trying to get Prolog to output people in the family trees nephew, as in nephew(X,Y) will list
X = the nephew
Y = Aunt/ Uncle
I've tried writing some of the code already, I'm pretty confident the son command works, and I believe sibling works, however combining these is proving, difficult.
parent(pam,bob).
parent(john,bob).
parent(john,liz).
parent(bob,ann).
parent(bob,pat).
parent(pat,jim).
parent(liz,joe).
parent(liz,tim).
parent(joe,kim).
female(pam).
female(liz).
female(ann).
female(pat).
female(zoe).
female(kim).
male(bob).
male(john).
male(jim).
male(joe).
male(tim).
sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \= Y.
son(X,Y) :- parent(Y, X), male(X).
nephew(X, Y) :- sibling(Y, Z), son(Z, X).
You were so close.
If you ran your query
nephew(X,Y) you would get
?- nephew(X,Y).
X = pam,
Y = liz ;
X = john,
Y = liz ;
X = liz,
Y = joe ;
X = liz,
Y = tim ;
false.
which as you note is wrong.
However if you rename your variables so that they are easier to comprehend
sibling(Child_a, Child_b) :-
parent(Parent, Child_a),
parent(Parent, Child_b),
Child_a \= Child_b.
son(Child,Parent) :-
parent(Parent, Child),
male(Child).
nephew(Parent, Child_a) :-
sibling(Child_a, Child_b),
son(Child_b, Parent).
you will see that sibling/2 and son/2 are correct and should see your problem with nephew/2.
When renaming variables, start at the facts and work back, do not start at the head of a clause and work down.
Another way that helps to figure out problems with multi-statement predicates is to run parts of them as separate queries, e.g.
?- sibling(Parent,Aunt_uncle),son(Nephew,Parent).
Parent = liz,
Aunt_uncle = bob,
Nephew = joe ;
Parent = liz,
Aunt_uncle = bob,
Nephew = tim ;
Parent = pat,
Aunt_uncle = ann,
Nephew = jim ;
false.
While this query gives more information than is needed, the correct values are in the results. Putting this in a predicate and returning only the desired values is all that is left to get this query working as desired.
A correct answer for nephew/2 is
nephew(Nephew,Aunt_uncle) :-
sibling(Parent,Aunt_uncle),
son(Nephew,Parent).
Example run:
?- nephew(Nephew,Aunt_uncle).
Nephew = jim,
Aunt_uncle = ann ;
Nephew = joe,
Aunt_uncle = bob ;
Nephew = tim,
Aunt_uncle = bob ;
false.
When writing code it is advisable to also create test cases.
If you are using SWI-Prolog then you can use the following test cases.
:- begin_tests(family).
% example of single test case
test(001) :-
sibling(bob,liz).
test(002) :-
son(tim,liz).
test(003,[nondet]) :-
nephew(tim,bob).
% example of test cases that test multiple variations for one predicate
sibling_test_case(bob,liz).
sibling_test_case(liz,bob).
sibling_test_case(ann,pat).
sibling_test_case(pat,ann).
sibling_test_case(joe,tim).
sibling_test_case(tim,joe).
test(004, [forall(sibling_test_case(Child_a,Child_b))]) :-
sibling(Child_a,Child_b).
son_test_case(bob,pam).
son_test_case(bob,john).
son_test_case(jim,pat).
son_test_case(joe,liz).
son_test_case(tim,liz).
test(005, [forall(son_test_case(Child,Parent)),nondet]) :-
son(Child,Parent).
nephew_test_case(joe,bob).
nephew_test_case(tim,bob).
nephew_test_case(jim,ann).
test(006, [forall(nephew_test_case(Nephew,Aunt_uncle)),nondet]) :-
nephew(Nephew,Aunt_uncle).
:- end_tests(family).
These are run with run_tests.
?- run_tests.
% PL-Unit: family ................. done
% All 17 tests passed
true.
See: Prolog Unit Tests
Getting started with prolog and I am trying to do some simple exercises, however I got stuck pretty early... What I am trying to 'say' is: mike likes anyone if it's a man or a woman and it is not mike. But the X \= mike seems to be ignored:
man(mike).
man(danny).
man(samuel).
man(henry).
woman(samantha).
woman(jane).
woman(betty).
woman(jenny).
likes(mike, X) :-
man(X);
woman(X),
X \= mike.
mike shows up...
?- findall(X, likes(mike, X), L).
L = [mike, danny, samuel, henry, samantha, jane, betty, jenny].
I assume it's because prolog evaluates the rule man(X) first so mike is true. However if I change it to:
likes(mike, X) :-
X \= mike,
man(X);
woman(X).
I will only get the women.
?- findall(X, likes(mike, X), L).
L = [samantha, jane, betty, jenny].
The question is how to do it then? Thanks!
Note: I am using swi-prolog
The problem is operator precedence. Just C/C#/Java/SQL and other procedural languages, in Prolog, logical OR (;) has a different precedence than does logical AND (',').
In most procedural languages, an expression like
A || B && C
Is parsed as
A || ( B && C )
In Prolog, an expression like yours
A ; B , C
is parsed as if written
A ; (B,C)
So your
likes(mike, X) :-
man(X);
woman(X),
X \= mike.
is essentially
likes(mike,X) :- man(X) ; ( woman(X) , X \= mike ) .
You are asserting that Mike likes anyone who is
a man (including himself), OR
a woman who is not mike.
When what you mean was that Mike likes anyone — male or female — other than himself.
So...you need to make the precedence explicit with parentheses,
likes(A,B) :- ( man(B) ; woman(B) ) , A \= B .
or better yet, break your predicate up into 2 clauses and make it generic:
likes(A,B) :- man(B) , A \= B .
likes(A,B) :- woman(B) , A \= B .
Improve things further by making gender an attribute of the entity (a person) rather than a fact in and of itself:
person( mike , male ) .
person( danny , male ) .
person( samuel , male ) .
person( henry , male ) .
person( samantha , female ) .
person( jane , female ) .
person( betty , female ) .
person( jenny , female ) .
Then likes/2 is even simpler, since one can like people of both genders:
likes(A,B) :- person(B,_) , A \= B .
Besides the OR-problem there is another problem. The predicate (\=)/2 is not a constraint, means its essentially not a fully declarative predicate.
The predicate is usually bootstrapped via negation as failure, and negation as failure itself is not fully declarative.
The predicate is bootstrapped from ordinary unification as follows:
X \= Y :- \+ X = Y.
An alternative definition would be:
X \= X :- !, fail.
_ \= _.
If you really want to be able to move around the inequality you should take a Prolog system with constraints and then resort to dif/2.
With dif/2 you can write:
likes(mike, X) :-
(dif(X, mike),
man(X)
; woman(X)).
Or the following:
likes(mike, X) :-
(man(X),
dif(X, mike)
; woman(X)).
And you will get the same results. This is not possible with (\=)/2.
Bye
P.S.: Reasons for the problems with (\=)/2. Negation as failure \+ A sometimes not only acts as ~A, but also as ~exists X1,..,Xn A. The interested reader might like to wade through the admittedly little old, but still applicable:
The Proof Theory of Logic Programs with Negation
Robert Stärk, Bern, 1992
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.29.2745&rep=rep1&type=pdf
The problem is with the OR operator (;) :
man(mike).
man(danny).
man(samuel).
man(henry).
woman(samantha).
woman(jane).
woman(betty).
woman(jenny).
Your solution:
likes(mike, X) :- (man(X);woman(X)),X\=mike.
Another solution (using an equivalent to the OR operator) could be:
likes(mike, X) :- man(X), X\=mike.
likes(mike, X) :- woman(X).
Notice that you don't need an OR operator.
Using recursion i need to find all blood relatives of any person in the family tree.
My attempt so far has failed.
Here is my code, with my attempt at the bottom
female(helen).
female(debbie).
female(louise).
female(yvonne).
female(belinda).
female(heather).
male(john).
male(andrew).
male(barry).
male(daniel).
male(charles).
parent(helen, debbie).
parent(helen, barry).
parent(helen, louise).
parent(john, debbie).
parent(john, barry).
parent(andrew, louise).
parent(debbie, yvonne).
parent(debbie, daniel).
parent(barry, charles).
parent(barry, belinda).
parent(louise, heather).
mother(X, Y) :-
female(X),
parent(X, Y).
father(X, Y) :-
male(X),
parent(X,Y).
child(X, Y) :-
parent(Y, X).
daughter(X, Y) :-
parent(Y, X),
female(X).
son(X, Y) :-
parent(Y,X),
male(X).
sister(X, Y) :-
female(X),
parent(Q,X),
parent(Q,Y).
brother(X, Y) :-
male(X),
parent(Q,X),
parent(Q,Y).
sibling(X, Y) :-
parent(Q,X),
parent(Q,Y),
X\=Y.
uncle(X, Y) :-
parent(P,Y),
brother(X,P).
aunt(X, Y) :-
parent(P,Y),
sister(X,P).
cousin(C, Cousin):-
parent(Parent,C),
sibling(Parent,AU),
child(Cousin,AU).
%Here is Relative
relative(An, Re):-
An\=Re,
parent(An, Re);
sibling(An, Re).
relative(An, Rela):-
parent(An, Child);
sibling(An, Rela),
relative(Child, Rela),
An\=Rela, C\=Rela.
Sort of works, but gets stuck in an infinite loop at the end.
Thanks.
not sure about 'relatives' (any person bound reachable in a parent/child relation ?), but your definition seems more complex than needed ( do you know what ; does ?).
I tried
relative(An, Re):-
parent(An, Re).
relative(An, Rela):-
parent(An, C),
relative(C, Rela).
that yields
16 ?- forall(relative(X,Y),writeln(X:Y)).
helen:debbie
helen:barry
helen:louise
john:debbie
john:barry
andrew:louise
debbie:yvonne
debbie:daniel
barry:charles
barry:belinda
louise:heather
helen:yvonne
helen:daniel
helen:charles
helen:belinda
helen:heather
john:yvonne
john:daniel
john:charles
john:belinda
andrew:heather
true.
edit I tried another relation, using a generalized parent/2, but still too permissive.
relative(Pers, Re):-
ancestor(Re, Pers) ; sibling(Pers, Re) ; cousin(Pers, Re) ; uncle(Re, Pers) ; aunt(Re, Pers).
ancestor(Anc, Pers) :- parent(Anc, Pers).
ancestor(Anc, Pers) :- parent(Anc, P), ancestor(P, Pers).
Maybe cousin/2 is too permissive also. Here is the graph
I guess that heather should have only luise,helen,andrew as relatives. It's this true ?
edit given latest comment, seems that the definition could be right. I get
24 ?- setln(X,relative(heather,X)).
andrew
barry
belinda
charles
daniel
debbie
helen
louise
yvonne
true.
that is everyone is related to heather apart john.
Here's one way that works, but it will sometimes produce duplicates. Using setof will give the unique collection. I avoided the miscellaneous relations and stuck with descendent or parent.
descendent(A, B) :-
parent(B, A).
descendent(A, B) :-
parent(C, A),
descendent(C, B).
relative(A, B) :-
descendent(B, A).
relative(A, B) :-
descendent(A, B).
relative(A, B) :-
descendent(A, C),
descendent(B, C),
A \= B.
setof(A, relative(heather, A), Relatives).
Relatives = [andrew,barry,belinda,charles,daniel,debbie,helen,louise,yvonne]
If you don't have setof, you can use the findall/3 and sort/2 ISO predicates:
findall(A, relative(heather, A), R), sort(R, Relatives).
Note that the solutions presented so far assume that all of the relatives have unique names. A general case of dealing with relatives with the same first name (and possibly the same last name) you would need to track and compare lineages for differences.
There are these facts:
man(john).
man(carl).
woman(mary).
woman(rose).
I need to create the predicate people(List), which returns a list with the name of every man and woman based on the previous facts. This is what I need as output:
?- people(X).
X = [john, carl, mary, rose]
And here is the code I wrote, but it's not working:
people(X) :- man(X) ; woman(X).
people(X|Tail) :- (man(X) ; woman(X)) , people(Tail).
Could someone please help?
Using findall/3:
people(L) :- findall(X, (man(X) ; woman(X)), L).
?- people(X).
X = [john, carl, mary, rose].
Here we go:
person(anne).
person(nick).
add(B, L):-
person(P),
not(member(P, B)),
add([P|B], L),!.
add(B, L):-
L = B,!.
persons(L):-
add([], L).