2

I have this program in prolog, where I basically define a graph of people and I need to make some predicates that will show me which people are connected and which are the cliques. Here are the facts:

graph([person(susan, [reed, jen, andrzej, jessica]),
   person(reed, [tony, jessica]),
   person(jessica, [jen,susan]),
   person(tony, []),
   person(ken, [andrzej]),
   person(jen, [tony, susan, jessica]),
   person(andrzej, [susan, ken])]).

And here is the definition of my predicate named clique. It takes as parameters the graph G, and a list of persons and tries to check if the person inside the list are indeed a clique of friends.(which means that the predicate goodfriends is true for each pair of persons inside the list)

clique(G, [FriendOne, FriendTwo]):-
  goodfriends(G, FriendOne, FriendTwo).
clique(G, [FriendOne | FriendList]):-
  atLeastTwoElements(FriendList),
  clique(G, FriendList),
  goodFriendWith(G, FriendOne, FriendList).

What clique does is: if the list consists of only two people then just check these two people if they are goodfriends. If this is true then there is a clique between the two of them. If there are more than two people in the list of people then for each head of the list i.e. person check if he is a good friend with the rest of the persons in the tail of the list, and do that recursively for all the list of persons. Below are defined the rest of the helper predicates for the clique to work.

goodfriends(G, FriendOne, FriendTwo):-
  getPerson(G, FriendOne, PersonOne),
  getPerson(G, FriendTwo, PersonTwo),
  hasFriend(PersonOne, FriendTwo),
  hasFriend(PersonTwo, FriendOne).

%% checks if this friend is goodfriend with all these friends
goodFriendWith(_, _, []).
goodFriendWith(G, FriendOne, [FriendTwo | FriendList]):-
  goodFriendWith(G, FriendOne, FriendList),
  goodfriends(G, FriendOne, FriendTwo).

%% gets specific person by a name from the graph
getPerson([person(Name, Friends)|_], Name, person(Name, Friends)).
getPerson([_|T], Name, Result):-
  getPerson(T, Name, Result).

%% checks if a person has a certain friend
hasFriend(person(_, Friends), Friend):-
  member_(Friend, Friends).

member_(X, [X|_]).
member_(X, [_|Tail]) :- member_(X, Tail).
atLeastOneElement([_|_]).
atLeastTwoElements([_,_|_]).

Question

When I create the predicate named runner to test the predicate "clique":

runner(R):-
  graph(G),  
  clique(G, R).

I want it to return all the cliques inside the graph but my result is:

?- runner(R).
R = [susan, jessica] ;
R = [susan, jen] ;
R = [susan, andrzej] ;
R = [jessica, susan] ;
R = [jessica, jen] ;
R = [ken, andrzej] ;
R = [jen, susan] ;
R = [jen, jessica] ;
R = [andrzej, susan] ;
R = [andrzej, ken] ;
R = [jen, susan, jessica] ;
R = [jessica, susan, jen] ;
R = [jen, jessica, susan] ;
R = [susan, jessica, jen] ;
R = [jessica, jen, susan] ;
R = [susan, jen, jessica] ;
ERROR: Out of local stack

What is wrong in the recursion? I know I get the correct results but for some reason after, all the results are shown it keeps recursing.

Thank you in advance.

false
  • 10,264
  • 13
  • 101
  • 209
sokras
  • 629
  • 1
  • 8
  • 19
  • Please add your definition of `atLeastTwoElements`. And why `member_` if you simply can state `member`? – false Sep 27 '14 at 14:18

1 Answers1

3

There is a simple debugging technique in a pure, monotonic program like yours. Simply add false goals into your program. If the resulting program still loops, the remaining visible part must be fixed. (There might be more errors, but as long as that part is not fixed you will not fix the problem)

Here is what I got so far:

runner(R):-
  graph(G),
  clique(G, R), false.

clique(G, [FriendOne, FriendTwo]):- false,
  goodfriends(G, FriendOne, FriendTwo).
clique(G, [FriendOne | FriendList]):-
  atLeastTwoElements(FriendList),
  goodFriendWith(G, FriendOne, FriendList), false,
  clique(G, FriendList).

Therefore, one or the one problem lies in goodFriendWith. I found it somewhat irritating that your definition succeeds for infinitely many lists like so:

?- length(L,N),
   maplist(=(jessica),L),
   goodFriendWith(
      [  person(susan,[reed,jen,andrzej,jessica]),
         person(reed,[tony,jessica]),
         person(jessica,[jen,susan]),
         person(tony,[]),
         person(ken,[andrzej]),
         person(jen,[tony,susan,jessica]),
         person(andrzej,[susan,ken])],
      susan,
      L).
   L = [], N = 0
;  L = [jessica], N = 1
;  L = [jessica,jessica], N = 2
;  L = [jessica,jessica,jessica], N = 3
;  L = [jessica,jessica,jessica,jessica], N = 4
;  L = [jessica,jessica,jessica,jessica,jessica], N = 5
; ... .

So for simply two persons, there is already an infinity of different solutions. That cannot terminate! You need to fix this.

Or, to put it even more precise:

?- goodFriendWith([person(susan,[jessica]),person(jessica,[susan])],susan,L).
   L = []
;  L = [jessica]
;  L = [jessica,jessica]
;  L = [jessica,jessica,jessica]
;  L = [jessica,jessica,jessica,jessica]
;  L = [jessica,jessica,jessica,jessica,jessica]
;  ... .

This goal must produce finitely many solutions. However, there are infinitely many.

Since you are surprised about the result, let's continue debugging. To make it even more evident, I will now add extra goals (=)/2. Those goals will specialize the program too:

goodFriendWith(_, _, []) :- false.
goodFriendWith(G, FriendOne, [FriendTwo | FriendList]):-
  FriendOne = susan,
  FriendTwo = jessica,
  goodfriends(G, FriendOne, FriendTwo),
  goodFriendWith(G, FriendOne, FriendList), false.

?- goodFriendWith([person(susan,[jessica]),person(jessica,[susan])],susan,L).

Again, the query loops! You really should see the problem now.

false
  • 10,264
  • 13
  • 101
  • 209
  • The clique you have added in your answer has no recursive call. Shouldn't there be a recursion for all the people in the list? – sokras Sep 27 '14 at 15:30
  • The clique you have added in your answer has no recursive call. Also what do you suggest is wrong with goodfriendswith? I don't see how there can be infinite solutions. I mean it 'iterates' through finite lists. – sokras Sep 27 '14 at 15:39
  • @sokras: It produces infinitely many solutions! That is very wrong if you expect it to terminate. – false Sep 27 '14 at 15:59
  • how can this produce infinitely many solution. I dont get it:/ The code for goodfriendswith is plain and simple. Iterate through a list, if it is empty then it ends, if not check the next element of the list and so on – sokras Sep 27 '14 at 16:03
  • I still don't see why this goes on infinite iteration. :/ I understand that it does go on infinite iteration the way I have it and I can see that I get the same result over and over again, but I don't understand WHY it goes on like this. I mean it shouldn't, should it? – sokras Sep 27 '14 at 16:49