Prolog不';目标重新排序后,不能终止
我目前正在研究Learn Prolog Now示例,对于其中一个示例,如果一个规则中有一个小小的更改,我的KB将耗尽本地堆栈。这是知识库:Prolog不';目标重新排序后,不能终止,prolog,backtracking,failure-slice,non-termination,Prolog,Backtracking,Failure Slice,Non Termination,我目前正在研究Learn Prolog Now示例,对于其中一个示例,如果一个规则中有一个小小的更改,我的KB将耗尽本地堆栈。这是知识库: byCar(auckland,hamilton). byCar(hamilton,raglan). byCar(valmont,saarbruecken). byCar(valmont,metz). byTrain(metz,frankfurt). byTrain(saarbruecken,frankfurt). byTrain(metz,pa
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).
这是正在讨论的查询,它已用完堆栈:
?- travel(valmont,losAngeles).
但是如果我把规则改成
travel(X,Y) :- travel(Z,Y), travel(X,Z).
然后它就起作用了
如果我跟踪查询,我会很快陷入这样的困境:
Redo: (17) travel(raglan, _6896) ? creep
Call: (18) byPlane(raglan, _6896) ? creep
Fail: (18) byPlane(raglan, _6896) ? creep
Redo: (17) travel(raglan, _6896) ? creep
Call: (18) travel(raglan, _6896) ? creep
Call: (19) byCar(raglan, _6896) ? creep
Fail: (19) byCar(raglan, _6896) ? creep
Redo: (18) travel(raglan, _6896) ? creep
Call: (19) byTrain(raglan, _6896) ? creep
Fail: (19) byTrain(raglan, _6896) ? creep
Redo: (18) travel(raglan, _6896) ? creep
Call: (19) byPlane(raglan, _6896) ? creep
Fail: (19) byPlane(raglan, _6896) ? creep
Redo: (18) travel(raglan, _6896) ? creep
...
但我不明白为什么。难道它不应该理解raglan是一个终端站,因此它必须再回溯一层吗
谢谢
编辑:我使用SWI Prolog
编辑:我一步一步地检查后发现了问题。
在拉格伦的案例中,根本没有任何地方的规则。因此,在尝试乘飞机、乘火车、乘汽车后,它再次尝试旅行(raglan,X)(上一条规则的第一个目标),从而循环。
但我看不出另一条规则有什么更好的地方。显然,在这种情况下,目标排序非常重要。正如你所发现的,你的第一个公式允许通过假设的另一个城市Z找到另一个从raglan到任何地方的假设连接,它的不存在永远不会被证明,因为你一直在寻找它,它会无限递归。说真的,trace是你最好的朋友,但这不是一件小事。您还必须考虑所有情况,其中一个、两个或没有一个参数是绑定的 您的第二个公式一点也不好,只是在不同的情况下碰巧失败了:
travel(losAngeles, valmont).
ERROR: Out of local stack
我建议通过区分直达和多站旅行,使您的逻辑更加安全:
connection(X,Y) :- byCar(X,Y).
connection(X,Y) :- byTrain(X,Y).
connection(X,Y) :- byPlane(X,Y).
travel(X,Y) :- connection(X,Y).
travel(X,Y) :- connection(X,Z), travel(Z,Y).
目标顺序现在不重要了,因为旅行总是需要一些物理连接(而不是递归)才能继续
这也使得记录旅程变得更容易,不管怎样,这都是您想要的(对吗?):
对于没有有效行程的情况:
travel(losAngeles, valmont, Journey).
false.
显然,在这种情况下,目标排序非常重要。正如你所发现的,你的第一个公式允许通过假设的另一个城市Z找到另一个从raglan到任何地方的假设连接,它的不存在永远不会被证明,因为你一直在寻找它,它会无限递归。说真的,trace是你最好的朋友,但这不是一件小事。您还必须考虑所有情况,其中一个、两个或没有一个参数是绑定的 您的第二个公式一点也不好,只是在不同的情况下碰巧失败了:
travel(losAngeles, valmont).
ERROR: Out of local stack
我建议通过区分直达和多站旅行,使您的逻辑更加安全:
connection(X,Y) :- byCar(X,Y).
connection(X,Y) :- byTrain(X,Y).
connection(X,Y) :- byPlane(X,Y).
travel(X,Y) :- connection(X,Y).
travel(X,Y) :- connection(X,Z), travel(Z,Y).
目标顺序现在不重要了,因为旅行总是需要一些物理连接(而不是递归)才能继续
这也使得记录旅程变得更容易,不管怎样,这都是您想要的(对吗?):
对于没有有效行程的情况:
travel(losAngeles, valmont, Journey).
false.
你需要澄清你所说的“它起作用”是什么意思。实际上,谓词
travel/2
的两个版本都没有终止。但我们碰巧找到了一个针对高度特定查询的解决方案
现在询问?-travel(汉密尔顿,洛杉矶)。
哪一个循环对两者都适用
因此,您的修复只适用于某些查询,而不适用于其他查询。难道没有更可靠的出路吗
一般来说,Prolog产生的非常精确的答案替换序列很难预测。您必须模拟Prolog所采取的每一小步
另一方面,有一个非常相关的概念叫做(通用)终止,它更容易预测,因为它独立于程序中的许多细节,比如事实出现的顺序。查询通用终止的最简单方法是在查询末尾添加目标false
但是你可以在任何你想要的地方进一步添加目标。这样一个修改过的程序称为。无论您如何插入false
以下内容均有效:
如果故障片没有终止,那么您的原始程序也不会终止
考虑以下两个变体的代码片段:代码>旅行/ 2 < /代码>:
1实际上,这只适用于纯单调程序。你的程序就是其中之一,你需要澄清你所说的“它有效”是什么意思。实际上,谓词
travel/2
的两个版本都没有终止。但我们碰巧找到了一个针对高度特定查询的解决方案
现在询问?-travel(汉密尔顿,洛杉矶)。
哪一个循环对两者都适用
因此,您的修复只适用于某些查询,而不适用于其他查询。难道没有更可靠的出路吗
一般来说,Prolog产生的非常精确的答案替换序列很难预测。您必须模拟Prolog所采取的每一小步
另一方面,有一个非常相关的概念叫做(通用)终止,它更容易预测,因为它独立于程序中的许多细节,比如事实出现的顺序。查询通用终止的最简单方法是在查询末尾添加目标false
但是你可以在任何你想要的地方进一步添加目标。这样一个修改过的程序称为。无论您如何插入false
以下内容均有效:
如果故障片没有终止,那么您的原始程序也不会终止
考虑以下两个变体的代码片段:代码>旅行/ 2 < /代码>:
1实际上,这只适用于纯单调程序。您的程序是非常有意义的程序之一!这也很好地进入了下一章