我刚刚开始学习Prolog并遇到有线问题:
假设我让人们排成两行:
left(a,b).
left(c,i).
left(i,j).
right(k,j).
代表这些行:
line1:a,b line2: c,i,j,k.
我想要的是看看这些人是否在同一条线上。我写的是遵循规则:
nextto(X,Y):- left(X,Y);left(Y,X);right(X,Y);right(Y,X).
sameline(X,Y):- nextto(X,Y).
sameline(X,Y):- nextto(X,Z),sameline(Z,Y).
我将“left/right
”转换为“nextto
”之间的关系。
当两个人实际上在相同的行中时,代码工作正常。但如果我尝试:
?- sameline(a,c)
“a”和“c”不在同一行。程序进入无限循环,直到用完本地堆栈。我假设它一直试图在“X”和“Y”之间找到变量“Z”,如果没有找到,程序将只创建另一个“Z”以继续搜索。我已经看到了使用递归调用的典型祖先示例:
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :- parent(X,Z),ancestor(Z,Y).
这是有道理的。但是我的代码与祖先的例子有何不同?显然nextto(a,j)
将返回false,但为什么循环不会停止?
这是我学习Prolog的第二天,因此高级使用Prolog可能无济于事。(。
感谢。
答案 0 :(得分:2)
如果你跟踪:
?- sameline(a,c).
由于以下原因,您会看到它属于周期a <-> b
nextto(X,Y):- left(X,Y);left(Y,X);right(X,Y);right(Y,X).
在上面的规则中,您允许next(a,b)
和next(b,a)
成功,但只需要一个成功就可以不进入周期:
在跟踪中,您可以看到:
Redo: (8) sameline(a, c) ? creep %OK trying to succeed sameline(a,c)
Call: (9) nextto(a, _884) ? creep
以上继续:
Call: (10) left(a, _884) ? creep
Exit: (10) left(a, b) ? creep %Only b succeeds left(a,_)
Exit: (9) nextto(a, b) ? creep
Call: (9) sameline(b, c) ? creep %OK trying to succeed recursively
相同线(B,C)。
但这最终要求:
Redo: (9) sameline(b, c) ? creep
Call: (10) nextto(b, _884) ? creep
Call: (11) left(b, _884) ? creep
Fail: (11) left(b, _884) ? creep
Redo: (10) nextto(b, _884) ? creep
Call: (11) left(_882, b) ? creep
Exit: (11) left(a, b) ? creep
Exit: (10) nextto(b, a) ? creep
Call: (10) sameline(a, c) ? creep % Same thing we began due to cycle
如果我理解你正在尝试做什么,为了解决这个问题,你应该定义:
nextto(X,Y):- left(X,Y);right(Y,X).
sameline(X,Y):- nextto(X,Y).
sameline(X,Y):- nextto(X,Z),sameline(Z,Y).
nextto/2
的这个定义强制遵循一条路径而不是循环。
示例:
?- sameline(c,X).
X = i ;
X = j ;
X = k ;
false.
?- sameline(c,i).
true ;
false.
?- sameline(a,c).
false.
如果你想nextto / 2来处理周期,解决它的一种方法就是保留你所访问的内容列表:
sameline(X,Y):- sameline(X,Y,[X]).
nextto(X,Y):- left(X,Y);left(Y,X);right(X,Y);right(Y,X).
sameline(X,Y,_):- nextto(X,Y).
sameline(X,Y,L):- nextto(X,Z),\+member(Z,L),sameline(Z,Y,[Z|L]).