如何解决在序言中难以思考无限递归问题
在阅读序言书时,我遇到了这个问题的麻烦。
% Write a predicate travel_to/2 which determines whether it is possible to
% travel from one place to another by chaining together car,train,and
% plane journeys. For example,your program should answer yes to the
% query travel_to(valmont,raglan).
by_car(auckland,hamilton).
by_car(hamilton,raglan).
by_car(valmont,saarbruecken).
by_car(valmont,metz).
by_train(metz,frankfurt).
by_train(saarbruecken,frankfurt).
by_train(metz,paris).
by_train(saarbruecken,paris).
by_plane(frankfurt,bangkok).
by_plane(frankfurt,singapore).
by_plane(paris,losAngeles).
by_plane(bangkok,auckland).
by_plane(singapore,auckland).
by_plane(losAngeles,auckland).
travel_to(X,Y) :- ( by_car(X,Y)
; by_train(X,Y)
; by_plane(X,Y)
).
travel_to(X,Y) :-
travel_to(X,Z),travel_to(Z,Y).
我很难知道无限递归的来源。我认为这段代码是这样的:“如果X可以直接行进到Y,那么我们满足谓词。否则让我们看看是否可以找到递归连接。X是否连接到Z然后再连接到Y?
滑动:
?- travel_to(valmont,raglan).
true ; % So it works?
ERROR: Stack limit (1.0Gb) exceeded
ERROR: Stack sizes: local: 0.9Gb,global: 84.2Mb,trail: 0Kb
ERROR: Stack depth: 11,028,501,last-call: 0%,Choice points: 12
ERROR: Probable infinite recursion (cycle):
ERROR: [11,501] user:travel_to(raglan,_22060066)
ERROR: [11,500] user:travel_to(raglan,_22060086)
这应该是错误的:
?- travel_to(losAngeles,metz).
ERROR: Stack limit (1.0Gb) exceeded
ERROR: Stack sizes: local: 0.9Gb,global: 84.1Mb,558,Choice points: 6
ERROR: Probable infinite recursion (cycle):
ERROR: [11,558] user:travel_to(raglan,_22059564)
ERROR: [11,557] user:travel_to(raglan,_22059584)
解决方法
问题在于您的第二个子句:
travel_to(X,Y) :-
travel_to(X,Z),travel_to(Z,Y).
将始终匹配。即使根本没有源于X
的目的地,travel_to/2
也会简单地依赖于递归子句。
即使我们设法解决该问题,仍然有可能进行无限递归,例如,如果存在by_car(a,b)
和by_train(b,a)
,那么prolog可能会搜索路径a - b - a - b - …
。
基本上有两个问题:
- 没有保证的进度,因此,如果没有
X
留下的行进路径,它仍会继续呼叫travel_to
;和 - 不能保证我们不会陷入循环。
例如,我们可以通过引入谓词step/2
来解决第一个问题:
step(X,Y) :-
by_car(X,Y).
step(X,Y) :-
by_train(X,Y) :-
by_plane(X,Y).
接下来我们可以做一个travel_to/2
谓词,它是step
的传递闭包:
travel_to(X,X).
travel_to(X,Z) :-
step(X,Y),travel_to(Y,Z).
此谓词可解决进度问题,因为对step/2
的调用迫使Prolog选择路径并因此跳了一步。但是它仍然可能会陷入循环。
我们可以通过维护已访问节点的列表来解决第二个问题,而拒绝第二次访问同一节点:
travel_to(X,Y) :-
travel_to(X,Y,[X]).
travel_to(X,X,_).
travel_to(X,Z,L) :-
step(X,\+ member(Y,L),[Y|L]).
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。