1

我希望我使用了一个可以理解的标题。我想要实现的是序言来确定关系,即使它没有明确说明。

brother(anders, jason).
sister(anders, madonna).
siblings(X,Y) :- sister(X,Y).
siblings(X,Y) :- brother(X,Y).
siblings(X,Y) :- siblings(X,Z), siblings(Z,Y).

这是我正在努力解决的兄弟姐妹规则。很明显杰森是安德斯的弟弟,麦当娜是安德斯的妹妹。杰森和麦当娜因此是安德斯的兄弟姐妹。即使没有明确说明,他们也是兄弟姐妹。如何通过检查兄弟姐妹的兄弟姐妹来使 prolog 确定这一点?这是我的最后一条规则,它会导致堆栈溢出 \o/ 这真的不是那么令人惊讶。这是否可以以递归方式实现,还是我需要编写更多规则,例如:

siblings(X,Y) :- sister(X,Z), brother(Z,Y).
siblings(X,Y) :- sister(X,Z), sister(Z,Y).
siblings(X,Y) :- brother(X,Z), sister(Z,Y).
siblings(X,Y) :- brother(X,Z), brother(Z,Y).

如果我们有一个非常大的家庭并且在我们的兄弟/姐妹事实中存在更大的差距,上述方法将不起作用,这可能意味着我必须检查一个人是否是我兄弟姐妹的兄弟姐妹等等。

这可能不是一个很好的现实世界的例子,但我正在寻找处理这些情况的概念,而不是解决特定问题的方法。

4

2 回答 2

2

给定适当的注释,它可能会在带有表格(YAP 或 XB)的 Prolog 中自动工作,但我们大多数人都生活在标准的 Prolog 的限制内。您的附加规则实际上只是将食物推到您的盘子上。你可以看到你需要更多的人来处理这样的情况:

brother(anders, brian).
brother(celeste, donal).
sister(brian, celeste).

如果您想sibling(anders, X)将 X 与donal您统一起来,您将需要一艘更大的船。

让我们称之为你的递归规则:

siblings(X,Y) :- siblings(X,Z), siblings(Z,Y).

这永远不可能成功,因为要想成功,就必须先成功。堆栈溢出没有发生,因为您正在siblings/2递归调用,它正在发生,因为它调用它两次以完成一次。它花费两美元来赚取一个。但是你可以通过花费你赚到的钱来收支平衡,用这个代替那个规则:

siblings(X, Z) :- brother(X, Y), siblings(Y, Z).
siblings(X, Z) :- sister(X, Y),  siblings(Y, Z).

这是一项资本改进,但还不够。这基本上让我们成为了兄弟姐妹。布赖恩是安德斯的兄弟,但安德斯不是布赖恩的兄弟。这个问题非常严重,以至于我们永远不会承认安德斯和多纳尔的兄弟情谊。我们可以通过添加更多规则来解决这个问题:

siblings(X, Y) :- brother(X, Y) ; brother(Y, Z).
siblings(X, Y) :- sister(X, Y)  ; sister(Y, X).

这些规则使兄弟情谊和姐妹情谊变得无方向。现在你得到了你想要的所有结果:

?- siblings(anders,Y).
Y = brian ;
Y = anders ;
Y = celeste ;
Y = donal ;
Y = brian ;
Y = celeste ;
false.

打赌你可以弄清楚如何消除自兄弟姐妹的问题。其他副本可能会更麻烦。试一试,如果您遇到困难,请告诉我们。

于 2013-08-12T04:57:22.557 回答
1

这是你想要的传递闭包:

sibl(A,B):- brother(A,B) ; brother(B,A) ; sister(A,B) ; sister(B,A).

sibl_trans(A,B):- sibl(A,B).
sibl_trans(A,B):- sibl(A,C), sibl_trans(C,B).

你的定义就像

sibl_trans(A,B):- sibl(A,B).
sibl_trans(A,B):- sibl_trans(A,C), sibl_trans(C,B).

这是左递归。不好,对 Prolog 进行深度优先搜索可以迭代深化一起工作。

这个问题经常出现。

于 2013-08-13T18:11:38.550 回答