views:

522

answers:

4

I have to simulate family tree in prolog. And i have problem of symetrical predicates. Facts:

parent(x,y).
male(x).
female(y).
age(x, number).

Rules:

blood_relation is giving me headache. this is what i have done:

blood_relation(X,Y):-ancestor(X,Y).
blood_relation(X,Y):-uncle(X,Y);brother(X,Y);sister(X,Y);(mother(Z,Y),sister(X,Z));(father(Z,Y),sister(X,Z));(father(Z,Y),brother(X,Z)).
blood_relation(X,Y):-uncle(X,Z),blood_relation(Z,Y).

and i am getting i think satisfactory results(i have double prints - can i fix this), problem is that i want that this relation be symetrical. It is not now.

blood_relation(johns_father, joh):yes 
blood_relation(john,johns_father): no

so..is there a way to fix this. And i need query: All pairs that are not in blood_relation..

+1  A: 

A bit looks like a homework, isn't it...

One trick which most of beginners of prolog don't think of is list pattern matching. Think of a tree like [a1,[[a2],[b2,[[e3],[f3]]],[c2]]] as in <tree>=[root,[<tree1>,<tree2>,...]]:

%Y is immediate child of X?
child(X,Y,[X|S]) :- member([Y|_],S).

%pick one tree in S and check
child(X,Y,[X|S]) :- member([Z|SS],S),child(Z,Y,[Z|SS]).

%X and Y end up with same root?
sib(X,Y,[R|T]) :- child(R,X,[R|T]), child(R,Y,[R|T]).

I think you can improve upon this like, using pairs as roots, adding genders, giving names to specific relations of members of the tree...

artificialidiot
tnx, i found something like this on web, just seemd complicated at the time. Im gonna take a closer look on this matter.
shake
A: 

What kinds of relationships is the first statement supposed to satisfy?

blood_relation(X,Y):-blood_relation(X,Y).

That isn't telling you anything that you don't already "know" and is going to cause you recursion headaches. As for the 'no' answer, is looks like you've already gotten all of the answers from the query that you are going to get, and the interpreter is just telling you that there aren't any more.

You really should post more facts, and the definition of uncle/2, and is there a reason why you're not matching a mother's brother, just her sister? You have lots of other issues to work on :-).

For everything that is not a blood relation, try this:

not_blood_relation(X, Y) :- blood_relation(X, Y), !, fail.
not_blood_relation(X, Y).

And ask yourself why it works!

Joel
A: 

What kinds of relationships is the first statement supposed to satisfy? blood_relation(X,Y):-blood_relation(X,Y).

sorry..it is a bad coppy/paste..it blood_relation(X,Y):-ancestor(X,Y).I fixed it now in original post.

here are other rules:

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

mother(X,Y):-parent(X,Y),female(X).

brother(X,Y):-parent(Z,X),parent(Z,Y),male(X).

sister(X,Y):-parent(Z,X),parent(Z,Y),female(X).

grandFather(X,Y):-parent(Z,Y),parent(X,Z),male(X).

grandMother(X,Y):-parent(Z,Y),parent(X,Z),female(X).

uncle(X,Y):-mother(Z,Y),brother(X,Z).

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

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

mothers brother is in uncle definition. Its kind of strange. I got rules that i need to implement, and i dont know do i can implement rules besides that. :S :S Im just confused.

Any idea how to make blood_relation symmetric?? and not_blood_relation is new rule, and i need querry. This one is really giving me headache. Maybe couse relation is written like crap. Damn! :))

And there is no more facts. Thats all. All rules, and all facts. :)

querry..not(blood_relation(X,Y)) doesnt work, and i really dont know why. For example querry:

age(X,Y), Y>18, not(parent(X,Z)),write(X),nl,fail.

works just fine

shake
+1  A: 

The naive solution to making a particular predicate symmetric isn't that far from a decent one. For the sake of generality, let's look at a friendship relation so people don't get tripped up on uncles and the like.

Here are some facts detailing a friendship relation (where, say, the numbers are user ids and the particular ordering of the arguments came from who initiated the friendship).

friends(1,2).
friends(5,2).
friends(7,4).

You'd initially think a rule like "friends(A,B) :- friends(B,A)." would fix things right up, but this leads you to infinite recursion because it tells prolog that if it just swaps the argument one more time it might just work. There is a predicate called "@</2" that tells you whether one term (even a variable) comes before another in the "standard order of terms". The technical meaning isn't all that important here, but what we care about is that for two different terms it is only true for one ordering of them. We can use this to break the infinite recursion!

This single rule will take care of making "friend/2" symmetric.

friends(A,B) :- A @< B, friends(B,A).

As neat as this is, there is an approach way you should take for large projects. Recall that the ordering of the args in my list of facts had some actual meaning (who initiated the friendship). Adding the final rule destroyed future access to this information and, for other people reading the code, hides the symmetric property in a single line of code which is easy to ignore in the face of a block of hard-coded data.

Condsider the industrial-strength solution:

friended(1,2).
friended(5,2).
friended(7,4).

friends(A,B) :- friended(A,B).
friends(A,B) :- friended(B,A).

It is bulkier, but it reads cleanly without using obscure predicates and retains the original information (which you might want again someday in a real application).

--

As for finding pairs that don't have a specific property, make sure you always include some predicate to provide context in your rule when you use negation to look for actual individuals.

potential_enemies(A,B) :- user(A), user(B), \+ friends(A,B).
rndmcnlly