【问题标题】:Why is this causing an infinite recursion?为什么这会导致无限递归?
【发布时间】:2014-09-28 00:18:24
【问题描述】:

我在 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 和人员列表作为参数,并尝试检查列表中的人是否确实是朋友圈。(这意味着谓词 goodfriends 对于列表中的每一对人都是正确的)

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

clique 所做的是:如果列表仅包含两个人,则只需检查这两个人是否是好朋友。如果这是真的,那么他们两个之间就有一个派系。如果人员列表中有两个以上的人,则对于列表的每个头部,即人员检查他是否与列表尾部的其余人员是好朋友,并递归地对所有列表执行此操作人。 下面定义了 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

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

提前谢谢你。

【问题讨论】:

  • 请添加您对atLeastTwoElements 的定义。如果您可以简单地声明member,为什么还要member_

标签: prolog failure-slice non-termination


【解决方案1】:

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

这是我目前得到的:

亚军(R):- 图(G), 集团(G,R),clique(G, [FriendOne, FriendTwo]):- false, 好朋友(G、FriendOne、FriendTwo)。 集团(G,[FriendOne | FriendList]):- atLeastTwoElements(朋友列表), goodFriendWith(G, FriendOne, FriendList), false, 集团(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 = 苏珊, FriendTwo = 杰西卡, 好朋友(G,FriendOne,FriendTwo), goodFriendWith(G, FriendOne, FriendList), false。 ?- goodFriendWith([person(susan,[jessica]),person(jessica,[susan])],susan,L)。

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

【讨论】:

  • 您在答案中添加的集团没有递归调用。不应该对列表中的所有人进行递归吗?
  • 您在答案中添加的集团没有递归调用。另外,您认为 goodfriendswith 有什么问题?我不明白怎么会有无限的解决方案。我的意思是它通过有限列表“迭代”。
  • @sokras:它产生了无限多的解决方案!如果您期望它终止,那是非常错误的。
  • 这怎么能产生无限多的解决方案。我不明白:/ goodfriendswith 的代码简单明了。遍历一个列表,如果为空则结束,如果不是则检查列表的下一个元素,依此类推
  • 我仍然不明白为什么会进行无限迭代。 :/ 我知道它确实按照我的方式进行了无限迭代,我可以看到我一遍又一遍地得到相同的结果,但我不明白为什么会这样。我的意思是不应该,应该吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
  • 2013-06-17
  • 1970-01-01
  • 2020-12-28
相关资源
最近更新 更多