Recursion 为什么我的prolog规则陷入无限递归

Recursion 为什么我的prolog规则陷入无限递归,recursion,prolog,transitive-closure,failure-slice,Recursion,Prolog,Transitive Closure,Failure Slice,我的代码按照预期的目的工作,但最后总是卡在循环中,给我一个错误,说超出了堆栈限制。我的代码如下: byCar(auckland,hamilton). byCar(hamilton,raglan). byCar(valmont,saarbruecken). byCar(valmont,metz). byTrain(metz,frankfurt). byTrain(saarbruecken,frankfurt). byTrain(metz,paris). byTrain(saarbrueck

我的代码按照预期的目的工作,但最后总是卡在循环中,给我一个错误,说超出了堆栈限制。我的代码如下:

byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).
   
byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).
   
byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).


travel(X,Y):- byCar(X,Y).
travel(X,Y):- byTrain(X,Y).
travel(X,Y):- byPlane(X,Y).
travel(X,Y):- travel(X,Z), travel(Z,Y).


当您调用travelmetz,To之类的东西时,travel/2的最后一个子句将使用新变量Z调用travelmetz,Z,然后可以使用新变量Z2调用travelmetz,Z2,依此类推

这个问题称为左递归:您有一个递归调用,它相当于一直向左的原始目标,即在子句的开头。解决方案是在递归调用之前取得一些进展。在这种情况下,可以在递归之前移动一跳:

step(X, Y) :-
    byCar(X, Y).
step(X, Y) :-
    byTrain(X, Y).
step(X, Y) :-
    byPlane(X, Y).

travel(X, Y) :-
    step(X, Y).
travel(X, Z) :-
    step(X, Y),
    travel(Y, Z).
现在终止:

?- travel(metz, To).
To = frankfurt ;
To = paris ;
To = bangkok ;
To = singapore ;
To = auckland ;
To = hamilton ;
To = raglan ;
To = auckland ;
To = hamilton ;
To = raglan ;
To = losAngeles ;
To = auckland ;
To = hamilton ;
To = raglan ;
false.

正如在的注释中指出的,您可以使用一般谓词来捕获此类闭包:。或者,一些Prolog系统提供了一种称为tabling的功能,您可以使用它来避免此类问题:

当您调用类似travelmetz的东西时,travel/2的最后一个子句将使用新变量Z调用travelmetz,Z,然后可以使用新变量Z2调用travelmetz,Z2,依此类推

这个问题称为左递归:您有一个递归调用,它相当于一直向左的原始目标,即在子句的开头。解决方案是在递归调用之前取得一些进展。在这种情况下,可以在递归之前移动一跳:

step(X, Y) :-
    byCar(X, Y).
step(X, Y) :-
    byTrain(X, Y).
step(X, Y) :-
    byPlane(X, Y).

travel(X, Y) :-
    step(X, Y).
travel(X, Z) :-
    step(X, Y),
    travel(Y, Z).
现在终止:

?- travel(metz, To).
To = frankfurt ;
To = paris ;
To = bangkok ;
To = singapore ;
To = auckland ;
To = hamilton ;
To = raglan ;
To = auckland ;
To = hamilton ;
To = raglan ;
To = losAngeles ;
To = auckland ;
To = hamilton ;
To = raglan ;
false.

正如在的注释中指出的,您可以使用一般谓词来捕获此类闭包:。或者,一些Prolog系统提供了一种称为tabling的功能,您可以使用它来避免此类问题:

byAnyX,Y:-byCarX,Y;byTrainX,Y;byPlaneX,Y。travelX,Y:-ClosureBayany,X,Y。请参阅闭包/3了解为什么它会循环。byAnyX,Y:-byCarX,Y;byTrainX,Y;byPlaneX,Y.travelX,Y:-ClosureBayany,X,Y.请参阅“关闭”部分/3有关它为什么循环的答案,请参阅。另一种方法是添加一个参数,跟踪您看到的位置,并使用“成员”检查列表中是否有新位置。教科书中有很多这样的例子,我不认为这是另一种方法。你不需要用基本相同的方法解决左递归问题吗?如果你能发布一个备选答案,那就太好了!如果你把travelX,Y:-travelX,Z,travelZ,Y.改为travelX,Seen0,Seen,Y:-\+memberX,Seen,travelX,[X|Seen],Seen1,Z,travelZ,[Z|Seen1],Seen,Y.,那么我认为它还没有测试过。制作表格要容易得多;当然,您也可以将该行更改为travelX,Y:-travel0X,Z,travelZ,Y。如果您定义travel0X,Y:-byCarX,Y等。本质上,查询分层是由手动完成的。另一种方法是添加一个参数,跟踪您看到的位置,并使用member/2检查列表中是否有新位置。教科书中有很多这样的例子,我不认为这是另一种方法。你不需要用基本相同的方法解决左递归问题吗?如果你能发布一个备选答案,那就太好了!如果你把travelX,Y:-travelX,Z,travelZ,Y.改为travelX,Seen0,Seen,Y:-\+memberX,Seen,travelX,[X|Seen],Seen1,Z,travelZ,[Z|Seen1],Seen,Y.,那么我认为它还没有测试过。制作表格要容易得多;当然,您也可以将该行更改为travelX,Y:-travel0X,Z,travelZ,Y。如果您定义travel0X,Y:-byCarX,Y等,本质上,查询分层是手工完成的