在列表列表中抛出本地堆栈错误

时间:2016-10-26 21:51:53

标签: prolog

我的输入:0和1的列表列表。 0是可步行街区,1是墙。我的起点(1,1)和我的结束点(现在我们将使用(2,2))。

我的输出:从1,1到2,2的路径。

我的问题:我正在访问索引之外的东西,我不明白为什么。

这是我的代码:

maze([[0, 1, 0, 0],
  [0, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 1, 0, 1],
  [0, 0, 0, 0]]).

checkfor(X,Y,Maze) :-
    nth1(Y, Maze, R),
    nth1(X, R, 0),
    assertz(w(X,Y)).

findadjacentsquare(OldX,OldY,X,Y,Maze) :-
    nextmove(OldX,OldY,X,Y),
    checkfor(X,Y,Maze).

nextmove(OldX,OldY,OldX,Y) :-
    Y is OldY+1.
    %Y >= 0,
    %Y < 5.
nextmove(OldX,OldY,X,OldY) :-
    X is OldX+1.
    %X >= 0,
    %X < 5.
nextmove(OldX,OldY,OldX,Y) :-
    Y is OldY-1.
    %Y >= 0,
    %Y < 5.
nextmove(OldX,OldY,X,OldY) :-
    X is OldX-1.
    %X >= 0,
    %X < 5.

citypath(X,Y,X,Y,Maze,Path) :-
  write(Path).
citypath(OldX,OldY,X,Y,Maze,Path) :-
  findadjacentsquare(OldX,OldY,X1,Y1,Maze),
  citypath(X1,Y1,X,Y,Maze,[w(X1,Y1)|Path]).

所以在这里打电话是我正在做的事情:

maze(M1), citypath(1,1,2,2,M1,Path)

现在我还没有任何逻辑来保存路径,但这不是问题。我的问题是,当我运行它时,它正在解决本地堆栈错误,我似乎无法找出原因。我尝试在下一个语句中对约束进行硬编码,但这似乎并没有起作用。

2 个答案:

答案 0 :(得分:3)

在您的定义中,最好删除assertz/1。并使用path/4添加。

nextstep(Maze,S0,S1) :-
   S0 = X0-Y0,
   S1 = X1-Y1,
   findadjacentsquare(X0,Y0,X1,Y1,Maze).

mazepath(Path) :-
   maze(Maze),
   path(nextstep(Maze), Path, 1-1,2-2).

| ?- mazepath(Path).
Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,4-3,4-2,4-1,3-1,3-2,2-2] ? ;
Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,4-3,4-2,3-2,2-2] ? ;
Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,3-2,2-2] ? ;
Path = [1-1,1-2,2-2] ? ;
no

因此,这里的想法是重用通用谓词进行路径查找。它处理循环检查的所有细节。

由于您尝试使用assert:这也很可能,但它需要大量额外开销。例如,您需要初始化状态。您需要确保代码保持可重入,以便可以同时进行多次搜索。等等......简而言之,保持代码清洁通常是一种更好的方法。

答案 1 :(得分:1)

  

&#34;现在我还没有任何逻辑来保存路径,但这不是问题&#34;

不, 这个问题。 具体来说,你进入堆栈错误的原因是因为代码的逻辑迫使你无限期地在位置(3,5)和(4,5)之间来回切换。

  • 当您在(3,5)时,第一个nextmove谓词失败,第二个谓词失败,因此您设置x = 4。移动成功,您继续计算从(4,5)开始的下一步行动。
  • 当您(4,5)时,前三个nextmove谓词失败,第四个成功失败,因此您设置x = 3。移动成功,您继续计算从(3,5)
  • 开始的下一步行动
  • 等等。

避免这种情况的方法是到目前为止检查您的路径,在checkfor谓词中,如果之前遇到此路径,则使当前移动失败。 (假设游戏规则禁止你两次访问同一个位置并且你试图找到非循环路线而不是 - 注意这只是非循环的,而不是必须是最短的)。

换句话说:

  • 您第一次在(3,5)位置时,第一个nextmove谓词失败,但第二个临时&#34;成功&#34;,所以你转到(4,5)
  • 从(4,5)前三个nextmove谓词失败,但第四个成功,试图将你推回(3,5)。但是,checkfor检测到它已经在路径中,并且失败。由于不再需要nextmove个谓词,因此整个findadjacentsquare谓词都会失败。
  • 这会让你一直回溯到错误的地方,即移动的第二个nextmove谓词(3,5),因为事实证明它没有成功。所有。因此,prolog现在将继续尝试第三个​​ nextmove谓词。你的棋子现在会向上移动到(3,4),移动成功,你继续从那里开始。

等。

<小时/> 其他杂项建议:

  • 使用guitracer尝试xpce;使整个调试过程非常容易可视化!
  • 你的assert声明在这里没用,什么都不做:没有理由通过断言新的事实来修改你的数据库,因为你以后的任何地方都没有独立检查这些事实。码。相反,您将统一的航点附加到累加器模式中的列表(这正是您应该做的,但与检查数据库中现有/断言的事实无关)。
  • 在您的查询时,传递未初始化的Path参数是没有意义的,因为您的基本情况&#39;谓词打印累积列表,这是一堆附加到初始输入的路标,因此您的初始输入需要是特定的。您应该传递一个空列表,或者至少是第一个点,即w(1,1)