3

I am very new to Prolog and I was given this assignment.

My code is as follows:

relatives(cindy,tanya).
relatives(tanya,alan).
relatives(alan,mike).
relatives(kerry,jay).
relatives(jay,alan).

isRelated(X,Y):-
    relatives(X,Y).
isRelated(X,Y):-
    relatives(X,Z),
    isRelated(Z,Y).

Simple enough. This shows that if:

?- isRelated(cindy,mike).

Prolog will return true. Now, I'm stuck on how to make it return true if:

?- isRelated(mike,cindy).

I've been trying to come up with ideas like if isRelated(Z,Y) returns false, then switch X and Y, and run isRelated again. But I'm not sure if Prolog even allows such an idea. Any hints or advice would be greatly appreciated. Thanks!

UPDATE:************************************

So I added:

isRelated(X,Y):-
    relatives(X,Y);
    relatives(Y,X).

That will satisfy "direct" relationships, but simply enough I found out that it doesn't satisfy indirect relationships.

I really want to do something like, if the initial query:

isRelated(mike,cindy)

fails, then try and see if the reverse is true by switching X and Y:

isRelated(cindy,mike)

That will definitely return true. I just don't know how to do this syntactically in Prolog.

false
  • 10,264
  • 13
  • 101
  • 209
cYn
  • 3,291
  • 6
  • 25
  • 43
  • A hint question: if `relatives(Y,X)` holds, are `X` and `Y` related? – Will Ness Feb 23 '12 at 06:36
  • Yes this is for homework. I am definitely not new to this site so I know not to ask for specific advice that might give away too much answers. I have been programming in C/Java for 2 years now but I recently just got started with Prolog. It is definitely a whole different animal here. – cYn Feb 23 '12 at 06:56
  • I've added the "homework" tag to your question, that's why I asked. :) Good luck taming Prolog, I esp. recommend "The Art of Prolog". When you get to the difference lists, skip over all the hype and just remember: it's just open-ended lists with the end pointer explicitly kept at hand, so it can be set when needed. Think of logvars as of named pointers, too, which can be set *only once* (before backtracking). Cheers. – Will Ness Feb 23 '12 at 07:08

3 Answers3

1

Further hint to those in the comments, as I can't leave comments yet: With your original set of rules and facts, isRelated(cindy,tanya) is true, but isRelated(tanya,cindy) is not, so you need to make isRelated(X,Y) symmetric; what simple addition to isRelated would achieve that?

Also, you could try drawing a graph of the relation relatives(X,Y), with an arrow from X to Y for all your base facts, and see if that helps you think about how the Prolog interpreter is going to attempt to satisfy a query.

  • Hmm, I like this idea. I have a question though. Let's say the query is: isRelated(cindy,tanya) That returns true. So does that mean Prolog exits the whole code now that it's true? Because I know that in Java/C++, returning and boolean (be it true or false) exits the function entirely. I don't know if this is the same case in Prolog. – cYn Feb 23 '12 at 07:01
  • @user1227566 you usually would run your Prolog query at some Prolog prompt. Say it's SWI-Prolog' `?- ` and you type `isRelated(tom,jerry).`, press enter, and it spews back some answer at you. Prolog is supended at this point. It stopped in the middle, it knows where it is right now. Now you can press dot `.` to stop, or `;` to attempt another go at it, and it will continue from the suspension point. If there's another answer, it'll show it to you too; etc. If not, it'll say `No`. Then the search is fully over. – Will Ness Feb 23 '12 at 07:19
  • Oooh no wonder I always have to press enter twice after a query. I didn't know that about Prolog. – cYn Feb 23 '12 at 07:24
  • Yes, by pressing Enter you're telling your Prolog system: "Thanks, I'm satisfied with what I want. Don't search any further". (and pressing the dot doesn't work at all, don't know what I was thinking about). – Will Ness Feb 23 '12 at 07:32
  • @Jason I tried adding a simple code that will work for direct relationships, but it didn't work for indirect relationship. I edited my original post. – cYn Feb 23 '12 at 07:39
  • @user1227566 have you tried making the second clause for `isRelated` symmetrical too? – Will Ness Feb 23 '12 at 08:02
  • @user1227566 the better way is to leave your original definition alone (which really is defining a transitive closure of `relatives` relation), and add another one *on top of it*, with a disjunction of two symmetrical calls in it. – Will Ness Feb 23 '12 at 08:08
1

So to answer your last question, you don't switch the values of X and Y in Prolog, like you would call swap(x,y) in C, say. The value held by a logic variable can not be changed explicitly, only back-tracked over. But you can easily use Y where you would use X, and vice versa:

somePred(X,Y):- is_it(X,Y).
somePred(X,Y):- is_it(Y,X).

This defines somePred predicate as a logical disjunction, an "OR". It can be written explicitly too, like

somePred(X,Y):- is_it(X,Y) ; is_it(Y,X).

Note the semicolon there. A comma , between predicates OTOH defines a conjunction, an "AND" (a comma inside a compound term just serves to delimit the term's "arguments").

Will Ness
  • 70,110
  • 9
  • 98
  • 181
1

YOu're almost there, you're just trying, I think, to cram too much stuff into one predicate.

Write the problem statement in English and work from that:

A relationship exists between two people, X and Y

  • if X and Y are directly related, or
  • if any direct relative of X, P, is related to Y.

Then it gets easy. I'd approach it like this:

First, you have your set of facts about relatives.

related( cindy, tanya ).
...
related( james, alan ).

Then, a predicate describing a direct relationship is terms of those facts:

directly_related( X , Y ) :- % a direct relationship exists 
  related(X,Y)               %   if X is related to Y
  .                          % ... OR ...
directly_related( X , Y ) :- % a direct relationship exists
  related(Y,X)               %   if Y is related to X
  .                          %

Finally, a predicate describing any relationship:

is_related(X,Y) :-        % a relationship exists between X and Y
  directly_related(X,Y)   %   if a direct relationship exists between them
  .                       % ... OR ...
is_related(X,Y) :-        % a relationship exists between X and Y
  directly_related(X,P) , %   if a direct relationship exists between X and some other person P
  is_related(P,Y)         %   and [recursively] a relationship exists between P and Y.
  .                       %

The solution is actually more complicated than this:

  1. The facts about relationships describe one or more graphs. More on graphs at http://web.cecs.pdx.edu/~sheard/course/Cs163/Doc/Graphs.html. What you're doing is finding a path from node X to Node Y in the graph.

  2. If the graphs described by the facts about relationships have one or more paths between X and Y, the above solution can (and will) succeed multiple times (on backtracking), once for every such path. The solution needs to be deterministic. Normallly, having established that two people are related, we're done: just because I have two cousins doesn't mean I'm related to my aunt twice.

  3. If the graph of relationships contains cycles (almost certainly true) such that a "circular" path exists: A → B → C → A …, the solution is susceptible to unlimited recursion. That means the solution needs to detect and deal with cycles. How might that be accomplished?

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135