微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在序言中难以思考无限递归问题

如何解决在序言中难以思考无限递归问题

在阅读序言书时,我遇到了这个问题的麻烦。

% 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 - …

基本上有两个问题:

  1. 没有保证的进度,因此,如果没有X留下的行进路径,它仍会继续呼叫travel_to;和
  2. 不能保证我们不会陷入循环。

例如,我们可以通过引入谓词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 举报,一经查实,本站将立刻删除。