Reputation: 33
I meet some problem when I try to implement
friends(mia, ellen).
friends(mia, lucy).
friends(X,Y) :-
friends(X,Z),
friends(Y,Z).
and when i ask ?- friends(mia, X).
, it run out of local stack.
Then I add
friends(ellen, mia) friends(lucy, mia)
I ask ?- friends(mia, X).
,it keeps replying X = mia
.
I can't understand, why it is recursive?
Upvotes: 3
Views: 551
Reputation: 18726
This clause of friends/2
is flawed:
friends(X,Y) :- friends(X,Z),friends(Y,Z).
Translate that into English: "If X
and Y
have a mutual friend Z
, then X
and Y
are friends."
Or, let's specialize, let X
be "me", let Y
be my neighbour "FooBert", and let Z
be "you": So if I am your friend and FooBert is your friend... does that make me and FooBert friends? I don't think so, I hate that guy---he always slams the door when he gets home. :)
I suggest you consider the algebraic properties that the relation friends/2
should have, the ones it may have, and ones it should not have. What about reflexivity, symmetry, anti-symmetry, transitivity?
Upvotes: 2
Reputation: 38799
First, two assumptions:
the actual code you wanted to write is the following one, with appropriate dots:
friends(mia,ellen).
friends(mia,lucy).
friends(X,Y) :-
friends(X,Z),
friends(Z,Y).
transivity holds: friends of friends are my friends too (I would rather model friendship as a distance: "A is near B" and "B is near C" does not necessarly imply "A is near C"). repeat's answer is right about figuring out first what you want to model.
Now, let's see why we go into infinite recursion.
So, what happens when we ask: friends(mia,X)
?
Y=ellen
(you ask for more solutions)Y=lucy
(you ask again for more solutions)friends(mia,Y)
holds for some variable Y
.Is there a variable Z
such that friends(mia,Z)
holds ?
Notice that apart from a renaming from Y
to Z
, we are asking the same question as step 1 above? This smells like infinite recursion, but let's see...
friends
, but then we fail because there is no friends(ellen,Y)
nor friends(lucy,Y)
, so...This problem is analogous to infinite Left recursion in context-free grammars.
Have two predicates:
known_friends/2
, which gives direct relationships.friends/2
, which also encodes transitivity
known_friends(mia,ellen).
known_friends(mia,lucy).
friends(X,Y) :- known_friends(X,Y).
friends(X,Y) :- known_friends(X,Z), friends(Z,Y).
Now, when we ask friends(mia,X)
, friends/2
gives the same answer as the two clauses of known_friends/2
, but does not find any answer for the transitive clause: the difference here is that known_friends
will make a little progress, namely find a known friend of mia
(without recursion), and try to find (recursively) if that friend is a friend of some other people.
If we add known_friends(ellen, bishop)
:-) then friends
will also find Y=bishop
, because:
known_friends(mia,ellen)
holds, andfriends(ellen,bishop)
is found recursively.If you add cyclic dependencies in the friendship graph (in known_friends
), then you will have an infinite traversal of this graph with friends
. Before you can fix that, you have to consider the following questions:
friends(X,Y) <=> friends(Y,X)
hold for all (X,Y)
?friends(X,X)
, for all X
?Then, you should keep a set of all seen people when evaluating friends
in order to detect when you are looping through known_friends
, while taking into account the above properties. This should not be too difficult too implement, if you want to try.
Upvotes: 2