Prolog中的寻路递归内存不足

Prolog中的寻路递归内存不足,prolog,Prolog,我编写这个prolog程序是为了在网格中找到所有可能的路径: travel([X,Y],[X,Y1]) :- Y1 is Y+1. travel([X,Y],[X,Y0]) :- Y0 is Y-1. travel([X,Y],[X1,Y]) :- X1 is X+1. move([X,Y],n,[X,Y1]) :- travel([X,Y],[X,Y1]). move([X,Y],s,[X,Y0]) :- travel([X,Y],[X,Y0]). move([X,Y],e,[X1,Y])

我编写这个prolog程序是为了在网格中找到所有可能的路径:

travel([X,Y],[X,Y1]) :- Y1 is Y+1.
travel([X,Y],[X,Y0]) :- Y0 is Y-1.
travel([X,Y],[X1,Y]) :- X1 is X+1.

move([X,Y],n,[X,Y1]) :- travel([X,Y],[X,Y1]).
move([X,Y],s,[X,Y0]) :- travel([X,Y],[X,Y0]).
move([X,Y],e,[X1,Y]) :- travel([X,Y],[X1,Y]).

safe([Xn,Yn],[Xg,Yg]) :-
  Xg >= Xn,
  Xn >= 0,
  Yg >= Yn,
  Yn >= 0. %next state should be whit-in grid

%% solve([X,Y],[TargetX,TargetY],[Xg,Yg],[FirstMove|OtherMoves])

solve([X,Y],[X,Y],_,[]).
solve([X,Y],[Xt,Yt],[Xg,Yg],[Fm|Om]) :-
  move([X,Y],Fm,[Xn,Yn]),
  safe([Xn,Yn],[Xg,Yg]),
  solve([Xn,Yn],[Xt,Yt],[Xg,Yg],Om).
对于
求解
,[X,Y]是当前位置。所以我的结束状态是当前位置等于目标位置。然而,当我运行它时,我遇到了内存不足错误。知道我做错了什么吗?感谢您的帮助

?- solve([1,2],[4,2],[3,4],P).
ERROR: Stack limit (1.0Gb) exceeded
ERROR:   Stack sizes: local: 0.5Gb, global: 0.4Gb, trail: 29.0Mb
ERROR:   Stack depth: 951,746, last-call: 0%, Choice points: 1,903,475
ERROR:   Possible non-terminating recursion:
ERROR:     [951,746] user:solve([length:2], [length:2], [length:2], _114212638)
ERROR:     [951,745] user:solve([length:2], [length:2], [length:2], [length:1|_114212704])

?- length(P,4),solve([1,2],[4,2],[3,4],P).
false.

?- length(P,5),solve([1,2],[4,2],[3,4],P).
false.

你的程序在南北运动之间无限循环。尝试删除
move
travel
中的south子句,它会起作用

要调试这是如何发生的,请尝试使用
trace
并查看对
solve
的递归调用,您可以看到发生了什么

   Exit: (15) move([1, 3], n, [1, 4]) ? 
   Call: (15) safe([1, 4], [4, 4]) ? s
   Exit: (15) safe([1, 4], [4, 4]) ? 
   Call: (15) solve([1, 4], [3, 4], [4, 4], _3490) ? 
   Call: (16) move([1, 4], _3804, [_3822, _3828]) ? s
   Exit: (16) move([1, 4], n, [1, 5]) ? 
   Call: (16) safe([1, 5], [4, 4]) ? s
   Fail: (16) safe([1, 5], [4, 4]) ? 
   Redo: (16) move([1, 4], _3804, [_3822, _3828]) ? s
   Exit: (16) move([1, 4], s, [1, 3]) ? 
   Call: (16) safe([1, 3], [4, 4]) ? s
   Exit: (16) safe([1, 3], [4, 4]) ? 
   Call: (16) solve([1, 3], [3, 4], [4, 4], _3806) ? 
   Call: (17) move([1, 3], _4326, [_4344, _4350]) ? s
   Exit: (17) move([1, 3], n, [1, 4]) ? 
   Call: (17) safe([1, 4], [4, 4]) ? s
   Exit: (17) safe([1, 4], [4, 4]) ? 
   Call: (17) solve([1, 4], [3, 4], [4, 4], _4328) ? 
   Call: (18) move([1, 4], _4642, [_4660, _4666]) ? s
   Exit: (18) move([1, 4], n, [1, 5]) ? 
   Call: (18) safe([1, 5], [4, 4]) ? s
   Fail: (18) safe([1, 5], [4, 4]) ? 
此外,在
move:-travel
中,您似乎正在对变量名进行模式匹配,这也不起作用
move(P1,n,P2)
将尝试南北子句,而不仅仅是第一个子句(尝试
move([2,2],s,X)
并查看第一个解决方案是北向移动)。这将起作用,但对于south子句,您将有无限递归

move([X,Y], n, [X,Y1]) :- Y1 is Y+1.
move([X,Y], s, [X,Y1]) :- Y1 is Y-1.
move([X,Y], e, [X1,Y]) :- X1 is X+1.

非常感谢!我通过使用trace命令看到了发生的事情。现在,如果没有其他选项,我必须找到一种“默认”方式来执行函数。有没有实施这个的想法?我尝试使用长度(P,2),求解([1,1],[2,2],[2,2],P)。在2x2网格上,这很容易,因为我知道只有两个步骤就有两个结果。但在一个更大的网格上,这变得非常困难。我不明白你所说的“没有其他选项时默认执行”是什么意思。当
solve
失败时,是否要执行其他操作?如果只想在找到单个解决方案后停止,请使用
一次/1
或剪切。但是,如果您想要更多的解决方案,那么您需要一个更通用的谓词来取消某些移动序列的资格。一条
n
不能跟在
s
之后。。。如果你想从中获得更多乐趣,试着选择一个
随机成员(D[n,s,e])
并尝试这个移动方向。一切都解决了!我引入了另一个列表来跟踪我所在的位置,以便排除重复项。再次感谢@排除重复是否意味着你的道路不能纵横交错?如果是这样的话,那么你仍然会错过一些非最优的解决方案(可能不是因为你不去西部)。