prolog 中的递归引用



我在尝试实现时遇到了一些问题

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)

  1. 第一条给出Y=ellen(您要求更多解决方案)
  2. 第二条给出了Y=lucy(您再次要求更多解决方案)
  3. 堆栈溢出!

让我们详细说明第三个条款:

  1. 我想知道friends(mia,Y)是否适用于某些可变Y.
  2. 是否有一个变量Z使得friends(mia,Z)成立?

    请注意,除了从Y重命名为Z之外,我们问的问题与上面的步骤 1 相同?这闻起来像无限递归,但让我们看看...

  3. 我们尝试了friends的前两个子句,但后来我们失败了,因为没有friends(ellen,Y)也没有friends(lucy,Y),所以......
  4. 我们
  5. 调用第三个子句是为了找出是否存在传递友谊,我们回到步骤 1 而没有进一步的进展 => 无限递归。

这个问题类似于上下文无关文法中的无限左递归。

修复

有两个谓词:

  1. known_friends/2,这给出了直接的关系。
  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).

把它翻译成英语:"如果XY有一个共同的朋友Z,那么XY就是朋友。

或者,让我们专业化,让X成为"我",让Y成为我的邻居"FooBert",让Z成为"你":所以如果我是你的朋友,FooBert是你的朋友......这让我和福伯特成为朋友吗?我不这么认为,我讨厌那个人---他回家时总是关上门。:)

我建议你考虑关系friends/2应该具有的代数性质,它可能具有的代数性质,以及不应该具有的代数性质。反身性、对称性、反对称性、传递性呢?

最新更新