prolog - Prolog 在目标重新排序后不会终止
问题描述
我目前正在研究 Learn Prolog Now 示例,对于一个练习,如果我对一条规则进行微小更改,我的 KB 会用完本地堆栈。这是知识库:
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
编辑:我一步步解决后发现了问题。在插肩的情况下,任何地方都没有规则。因此,在尝试之后byPlane, byTrain, byCar
,它travel(raglan, X)
再次尝试(最后一条规则的第一个目标),从而循环。但我看不出另一条规则有什么更好的地方。
解决方案
您需要澄清“它有效”的含义。事实上,谓词的两个版本travel/2
都不会终止。但是碰巧找到了针对高度特定查询的解决方案。
现在询问?- travel(hamilton, losAngeles).
两者的循环。
因此,您的修复仅适用于某些查询,但不适用于其他查询。没有更可靠的出路吗?
一般来说,Prolog 产生的非常精确的答案替换序列很难预测。您将不得不模拟 Prolog 采取的每一个微小步骤。
另一方面,有一个非常相关的概念称为(通用)终止,它更容易预测,因为它独立于程序中的许多细节,例如事实出现的顺序。查询通用终止的最简单方法是在false
查询末尾添加目标。
false
但是你可以在任何你想要的地方进一步添加目标1。这种修改后的程序称为故障片。无论您如何插入false
以下保持:
如果故障片没有终止,那么您的原始程序也不会终止。
现在考虑 的两个变体的故障切片travel/2
:
travel(X,Y) :- false , byCar(X,Y)。travel(X,Y) :- false , byTrain(X,Y)。travel(X,Y) :- false , byPlane(X,Y)。 旅行(X,Y):-旅行(X,Z),假,旅行(Z,Y)。
还有你的另一个版本:
travel(X,Y) :- false , byCar(X,Y)。travel(X,Y) :- false , byTrain(X,Y)。travel(X,Y) :- false , byPlane(X,Y)。 旅行(X,Y):-旅行(Z,Y),假,旅行(X,Z)。
两者都没有,也X
没有Y
被考虑!所以这两个参数不影响终止。因此两个版本都不会终止。也就是说,它们永远不会终止。
现在将此结论与查看迹线的更传统方法进行比较。虽然故障切片允许我们做出一般性结论(“...永不终止”),但特定跟踪只能向您显示特定执行的详细信息。
为了解决这个问题,您需要更改可见部分中的某些内容。我的建议是使用closure/3
. 那是:
travel(X, Y) :-
closure(connexion, X, Y).
connexion(X,Y) :- byCar(X,Y).
connexion(X,Y) :- byTrain(X,Y).
connexion(X,Y) :- byPlane(X,Y).
还是用比较通用的path/4
。
1 实际上,这只适用于纯单调程序。你的程序就是其中之一
推荐阅读
- javascript - 当我输入四位数字时,会添加“/”,但是当我模糊时,“/”会消失
- wordpress - 从网站中提取价格的 XPath 表达式
- python - 如何在熊猫数据框中进行复杂计算
- java - java.lang.ClassNotFoundException:升级库后的 java.lang.constant.Constable
- primefaces - Primefaces时间线排序同一日期的事件
- laravel - 为什么我在 yahoo.com 帐户上收不到 laravel 发送的电子邮件?
- android - APIGEE API 的“缺少必填字段”响应
- android - 带有 DigitsKeyListener 和 inputType 的 keyListener 不能以编程方式协同工作
- python - 如何分隔逗号分隔的字符串点
- python - 查找所有超链接 ( , ) 并创建一个列表