search - 需要递归循环退出语句
问题描述
这是一个简单的递归序言示例。我不知道在哪里以及或多或少如何声明退出语句。test flight(sofia, dublin) 应该返回 true,但它会在最后一步检查你是否可以 directFlight(dublin, dublin)。这是代码:
directFlight(sofia, varna).
directFlight(sofia, paris).
directFlight(sofia, london).
directFlight(london, edinburg).
directFlight(paris, new_york).
directFlight(new_york, seattle).
directFlight(london, dublin).
flight(City1, City2) :-
directFlight(City1, City3),
flight(City3, City2).
输出:
[trace] ?- flight(sofia, dublin).
Call: (8) flight(sofia, dublin) ? creep
Call: (9) directFlight(sofia, _878) ? creep
Exit: (9) directFlight(sofia, varna) ? creep
Call: (9) flight(varna, dublin) ? creep
Call: (10) directFlight(varna, _878) ? creep
Fail: (10) directFlight(varna, _878) ? creep
Fail: (9) flight(varna, dublin) ? creep
Redo: (9) directFlight(sofia, _878) ? creep
Exit: (9) directFlight(sofia, paris) ? creep
Call: (9) flight(paris, dublin) ? creep
Call: (10) directFlight(paris, _878) ? creep
Exit: (10) directFlight(paris, new_york) ? creep
Call: (10) flight(new_york, dublin) ? creep
Call: (11) directFlight(new_york, _878) ? creep
Exit: (11) directFlight(new_york, seattle) ? creep
Call: (11) flight(seattle, dublin) ? creep
Call: (12) directFlight(seattle, _878) ? creep
Fail: (12) directFlight(seattle, _878) ? creep
Fail: (11) flight(seattle, dublin) ? creep
Fail: (10) flight(new_york, dublin) ? creep
Fail: (9) flight(paris, dublin) ? creep
Redo: (9) directFlight(sofia, _878) ? creep
Exit: (9) directFlight(sofia, london) ? creep
Call: (9) flight(london, dublin) ? creep
Call: (10) directFlight(london, _878) ? creep
Exit: (10) directFlight(london, edinburg) ? creep
Call: (10) flight(edinburg, dublin) ? creep
Call: (11) directFlight(edinburg, _878) ? creep
Fail: (11) directFlight(edinburg, _878) ? creep
Fail: (10) flight(edinburg, dublin) ? creep
Redo: (10) directFlight(london, _878) ? creep
Exit: (10) directFlight(london, dublin) ? creep
Call: (10) flight(dublin, dublin) ? creep
Call: (11) directFlight(dublin, _878) ? creep
Fail: (11) directFlight(dublin, _878) ? creep
Fail: (10) flight(dublin, dublin) ? creep
Fail: (9) flight(london, dublin) ? creep
Fail: (8) flight(sofia, dublin) ? creep
false.
问题出在:失败:(10)飞行(都柏林,都柏林)?蠕变。任何想法如何解决这一问题?
解决方案
不要考虑需要退出语句的循环。事实上,根本不要使用调试器,即使你知道 Prolog 处理器中发生了什么,它也会非常令人困惑。
您从由一组边(关系)连接的节点网络开始。
在这种情况下,节点由原子(表示城市)表示,关系称为 的边集directFlight/2
。
现在您要覆盖另一组边缘,称为flight/2
.
所以你必须问自己,我什么时候flight/2
在两个原子之间有边A
?B
有两种情况:
- 如果
directFlight/2
他们之间有一个(基本情况) - 如果有一个中间节点
I
,使得有一个flight/2
fromA
toI
和一个directFlight/2
betweenI
andB
。 - 或者,如果存在中间节点
I
,则存在directFlight/2
betweenA
andI
和flight/2
fromI
toB
。
Prolog 会在flight/2
被要求时自行找到边缘
flight(sofia, dublin).
(就像关系数据库查找 SQL 查询的结果一样)但是您必须注意终止。上面的替代情况(3)将导致搜索成功或“错误”。情况 (2) 将导致不终止 - 完全是由于 Prolog 的搜索策略(必须决定现实世界的机器如何通过网络搜索,在这种情况下,深度优先,最左边优先)。
这是flight/2
(第一个图像)的基本情况,所有flight/2
由 1 个调用深度的递归推导,以及所有flight/2
由 2 个调用深度的递归推导。
所以:
directFlight(sofia, varna).
directFlight(sofia, paris).
directFlight(sofia, london).
directFlight(london, edinburg).
directFlight(paris, new_york).
directFlight(new_york, seattle).
directFlight(london, dublin).
flight(City1, City2) :- directFlight(City1, City2).
flight(City1, City2) :- directFlight(City1, City3), flight(City3, City2).
接着:
?- flight(sofia,dublin).
true ;
false.
?- flight(sofia,X).
X = varna ;
X = paris ;
X = london ;
X = new_york ;
X = seattle ;
X = edinburg ;
X = dublin ;
false.
?- flight(X,sofia).
false.
附录 1
上面的程序可以阅读:
- “从LEFT :- 到 RIGHT ”(正如 Prolog 所做的那样)。它是为了确定一个
flight/2
事实是否成立,或者是否可以找到使其成为真的值。 - “从右-:到左”。心理形象是不断积累新的事实,
flight/2
直到一个人将它们全部收集起来并且不再添加任何东西:自下而上的搜索(这对大脑来说更容易,至少对我来说)。比搜索更安全,因为您不会因为子句以一种不幸的方式排列而冒着遇到无限递归天坑的风险,这也是一些 Datalog 实现所做的。
逻辑解读是一个(或两个)语句(“程序”),它给出了关于flight/2
应该如何构建的约束:
∀(City1, City2) :
(flight(City1, City2) <=
directFlight(City1, City2))
∧
∀(City1, City2) :
(flight(City1, City2) <=
(∃City3: directFlight(City1, City3) ∧ flight(City3, City2))
请注意,上面没有任何内容可以排除flight(X,Y)
可能由于上述原因之外的其他原因。然而,我们假设我们知道关于何时flight(X,Y)
成立的一切:封闭世界假设。
附录 2
人们经常忘记的一点是根本不需要递归调用。递归可以“展开”并且边到边的链接明确:
directFlight(sofia, varna).
directFlight(sofia, paris).
directFlight(sofia, london).
directFlight(london, edinburg).
directFlight(paris, new_york).
directFlight(new_york, seattle).
directFlight(london, dublin).
flight(City1, City2) :- directFlight(City1, City2).
flight(City1, City2) :- directFlight(City1, Ia),
directFlight(Ia, City2).
flight(City1, City2) :- directFlight(City1, Ia),
directFlight(Ia, Ib),
directFlight(Ib, City2).
flight(City1, City2) :- directFlight(City1, Ia),
directFlight(Ia, Ib),
directFlight(Ib, Ic),
directFlight(Ic, City2).
没有一个城市相距超过 3 跳,因此上述程序将找到所有flight/2
连接。
事实上,另一个练习是生成上述程序,将其作为要考虑的“最大深度”的参数。
推荐阅读
- javascript - 如何根据所选日期更改 UI 和保存数据?
- css - 响应式圆形卡片布局
- c# - 如何将参数(id)从页面视图传递到页面模型
- c# - 尽管实现了 INotifyPropertyChanged,但 WPF 视图未更新(.NET 5.0)
- python - 如何从 Cython 将 void 指针(数组的开头)传递给 C 函数?
- python - 如何在 keras ResNet 之后应用 Dense 层?
- sql - 按月计算日期范围的日历天数
- java - 我想创建一个类似 Instagram 的评论页面,但不能在 child() 中为参数“pathString”传递 null
- c++ - 通过右值传递给 unordered_map 时释放内存
- java - Android Instrumented 测试 - 模拟位置不起作用(尽管设置了 testLocation,但返回 null)