我在尝试实现时遇到了一些问题
friends(mia, ellen).
friends(mia, lucy).
friends(X,Y) :-
friends(X,Z),
friends(Y,Z).
当我问?- friends(mia, X).
时,它会用完本地堆栈。
然后我添加
friends(ellen, mia) friends(lucy, mia)
我问?- friends(mia, X).
,它一直在回复X = mia
。
我不明白,为什么它是递归的?
首先,两个假设:
-
您要编写的实际代码如下,带有适当的点:
friends(mia,ellen). friends(mia,lucy). friends(X,Y) :- friends(X,Z), friends(Z,Y).
-
传递性成立:朋友的朋友也是我的朋友(我宁愿将友谊建模为距离:"A 在 B 附近"和"B 在 C 附近"并不一定意味着"A 在 C 附近")。 Repeat的答案是正确的,首先要弄清楚你想要建模什么。
现在,让我们看看为什么我们要进入无限递归。
循序渐进
那么,当我们问:friends(mia,X)
- 第一条给出
Y=ellen
(您要求更多解决方案) - 第二条给出了
Y=lucy
(您再次要求更多解决方案) - 堆栈溢出!
让我们详细说明第三个条款:
- 我想知道
friends(mia,Y)
是否适用于某些可变Y
. 是否有一个变量
Z
使得friends(mia,Z)
成立?请注意,除了从
Y
重命名为Z
之外,我们问的问题与上面的步骤 1 相同?这闻起来像无限递归,但让我们看看...- 我们尝试了
friends
的前两个子句,但后来我们失败了,因为没有friends(ellen,Y)
也没有friends(lucy,Y)
,所以......
我们 - 调用第三个子句是为了找出是否存在传递友谊,我们回到步骤 1 而没有进一步的进展 => 无限递归。
这个问题类似于上下文无关文法中的无限左递归。
修复
有两个谓词:
known_friends/2
,这给出了直接的关系。friends/2
,也编码传递性known_friends(mia,ellen). known_friends(mia,lucy). friends(X,Y) :- known_friends(X,Y). friends(X,Y) :- known_friends(X,Z), friends(Z,Y).
现在,当我们问friends(mia,X)
时,friends/2
给出了与known_friends/2
的两个子句相同的答案,但没有找到传递子句的任何答案:这里的区别在于known_friends
会取得一点进展,即找到一个已知的mia
朋友(没有递归),并尝试(递归地)找到该朋友是否是其他人的朋友。
朋友的朋友
如果我们添加known_friends(ellen, bishop)
:-),那么friends
也会找到Y=bishop
,因为:
known_friends(mia,ellen)
成立,并且- 递归找到
friends(ellen,bishop)
。
圆
如果你在友谊图中添加循环依赖关系(在known_friends
年),那么你将无限遍历这个图,friends
.在解决此问题之前,您必须考虑以下问题:
friends(X,Y) <=> friends(Y,X)
适用于所有(X,Y)
吗?- 对于所有
X
来说,friends(X,X)
呢?
然后,在评估friends
时,您应该保留一组所有见过的人,以便检测何时循环known_friends
,同时考虑上述属性。如果你想尝试的话,这应该不会太难太实现。
friends/2
的这个子句是有缺陷的:
friends(X,Y) :- friends(X,Z),friends(Y,Z).
把它翻译成英语:"如果X
和Y
有一个共同的朋友Z
,那么X
和Y
就是朋友。
或者,让我们专业化,让X
成为"我",让Y
成为我的邻居"FooBert",让Z
成为"你":所以如果我是你的朋友,FooBert是你的朋友......这让我和福伯特成为朋友吗?我不这么认为,我讨厌那个人---他回家时总是关上门。:)
我建议你考虑关系friends/2
应该具有的代数性质,它可能具有的代数性质,以及它不应该具有的代数性质。反身性、对称性、反对称性、传递性呢?