为什么这会导致无限递归?

时间:2014-09-27 14:04:52

标签: prolog failure-slice non-termination

我在prolog中有这个程序,我基本上定义了一个人的图表,我需要制作一些谓词,告诉我哪些人是连接的,哪些是派系。以下是事实:

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])]).

这是我的谓词clique的定义。它将图G和人员列表作为参数,并尝试检查列表中的人是否确实是一群朋友。(这意味着谓词好朋友对于列表中的每对人都是如此)

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

集团的作用是:如果列表中只有两个人,那么只要检查这两个人是否是好朋友。如果这是真的那么他们两个之间就有一个集团。如果人员列表中有两个以上的人,那么列表的每个负责人,即人员检查他是否是列表尾部其余人员的好朋友,并递归地为所有列表人。 下面定义了clique工作的其余辅助谓词。

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([_,_|_]).

问题

当我创建名为runner的谓词来测试谓词“clique”时:

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

我想让它返回图表中的所有派系,但结果是:

?- 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

递归有什么问题?我知道我得到了正确的结果,但由于某种原因,所有结果都显示出它不断递归。

提前谢谢。

1 个答案:

答案 0 :(得分:3)

在像你这样的纯粹单调程序中有一种简单的调试技术。只需将 false 目标添加到您的计划中即可。如果生成的程序仍然循环,则必须修复剩余的可见部分。 (可能存在更多错误,但只要该部分未修复,您将无法解决问题)

这是我到目前为止所得到的:

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).

因此,goodFriendWith中存在一个或一个问题。我发现你的定义对于无限多的列表来说是成功的,这有点令人恼火:

?- 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 ...

因此,对于仅仅两个人来说,已经存在无限的不同解决方案。那不能终止!你需要解决这个问题。

或者,更准确地说:

?- 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] 

这个目标必须产生有限的许多解决方案。但是,有无数的。

由于您对结果感到惊讶,让我们继续调试。为了使其更加明显,我现在将添加额外的目标(=)/2。这些目标也将专门化该计划:

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).

再次,查询循环!你现在真的应该看到问题了。