【问题标题】:Nephew of my own father in prolog序言中我父亲的侄子
【发布时间】:2017-11-04 00:30:07
【问题描述】:

我正在用 prolog 编写一个家庭,但我在执行侄子时遇到了麻烦。当我询问 erick 是否是 Alberto 的侄子时,它返回 true,而当它应该返回 false 因为 Alberto 是 erick 的父亲时,它确实适用于所有其他应该为 true 的情况。如果有人可以帮助我,我将不胜感激。 我的代码:

man(beto).
man(fransisco).
man(alberto).
man(jaime).
man(manolo).
man(nolo).
man(lito).
man(manuel).
man(erick).
man(jesu).
man(jesus).
woman(emi).
woman(harumi).
woman(haru).
woman(yuneisi).
woman(yasmeli).
woman(mioara).
woman(elia).
woman(iza).
woman(alice).
woman(ofelia).
woman(arlet).
parent(manuel, alberto).
parent(ofelia, alberto).
parent(manuel, jaime).
parent(ofelia, jaime).
parent(manuel, manolo).
parent(ofelia, manolo).
parent(alberto, erick).
parent(alberto, beto).
parent(alberto, fransisco).
parent(emi, erick).
parent(emi, beto).
parent(manolo, nolo).
parent(manolo, arlet).
parent(nolo, lito).
parent(iza, lito).
parent(mioara, yuneisi).
parent(mioara, yasmeli).
parent(jaime, yuneisi).
parent(jaime, yasmeli).
parent(jesus_padre, jesu)
parent(jesus_padre, alice).
parent(jesus_padre, haru).
parent(harumi, haru).
parent(harumi, jesu).
parent(harumi, alice).
father(X,Y) :- parent(X,Y), man(X).
mother(X,Y) :- parent(X,Y), woman(X).
brother(X,Y) :- man(X), parent(F, X), parent(F, Y).
sister(X,Y) :- woman(X), parent(P, X), parent(P, Y).
grandpa(X,Y) :- father(F,Y), father(X,F), man(X).
grandma(X,Y) :- father(F,Y), mother(X,F), woman(X).
son(X,Y) :- father(Y,X), man(X).
nephew(X,Y) :- father(F,X), brother(F,Y).

【问题讨论】:

  • 您的代码不应该工作,因为您在 "parent(jesus_padre, jesu)" 行有语法错误,您忘记在末尾添加一个点。它现在应该可以工作了,还有一个提示:输入“guitracer”,然后输入“tracer”,如果你有任何错误,你可以看到它失败的地方

标签: prolog logic


【解决方案1】:

除了@LuaiGhunim 指出的parent(jesus_padre, jesu) 之后缺少的点之外,您的谓词还有一些其他问题。你对 Brother/2 的定义太笼统了。没有人是他自己的兄弟,但如果你查询你的谓词,你会发现几个这样的例子:

?- brother(X,X).
X = beto ;
X = beto ;
X = fransisco ;
X = alberto ;
X = alberto ;
X = jaime ;
X = jaime ;
X = manolo ;
X = manolo ;
X = nolo ;
X = lito ;
X = lito ;
X = erick ;
X = erick ;
X = jesu ;
X = jesu ;
false.

您可以通过添加目标 dif/2 轻松解决此问题:

brother(X,Y) :-
   dif(X,Y),
   man(X),
   parent(F, X),
   parent(F, Y).

现在上面的查询失败了:

?- brother(X,X).
false.

你仍然会得到很多双:

?- brother(X,Y).
X = beto,               % <- 1st occurrence
Y = erick ;             % <- 1st occurrence
X = beto,
Y = fransisco ;
X = beto,               % <- 2nd occurrence
Y = erick ;             % <- 2nd occurrence
.
.
.

这样做的原因是您可以通过母亲或父亲获得它。在上面的示例中(betoerick),您将通过emialberto 到达那里。这些解决方案可能是多余的,但它们是正确的。你的谓词 Sister/2 也是如此:

?- sister(X,X).
X = haru ;
X = haru ;
X = yuneisi ;
X = yuneisi ;
X = yasmeli ;
X = yasmeli ;
X = alice ;
X = alice ;
X = arlet.

补救方法同上:

sister(X,Y) :-
   dif(X,Y),
   woman(X),
   parent(P, X),
   parent(P, Y).

?- sister(X,X).
false.

?- sister(X,Y).
X = haru,
Y = jesu ;
X = haru,
Y = alice ;
X = haru,
Y = jesu ;
.
.
.

另一方面,您对 grandma/2 和 grandpa/2 的定义过于具体。要查看这一点,让我们将以下事实添加到您的代码中:

man(m1).
man(m2).

woman(w1).
woman(w2).
woman(w3).

parent(m1,w1).
parent(w1,w2).
parent(w2,w3).

那么下面的查询应该会成功,但是却失败了:

?- grandpa(m1,w2).
false.

?- grandma(w1,w3).
false.

原因是你定义中的中间父级grandpa/2 和grandma/2 是father/2,它应该是parent/2。此外,最后一个目标(man(X)woman(X))是多余的,因为它们已经分别被父亲/2 和母亲/2 覆盖。相反,您可以像这样定义两个谓词:

grandpa(X,Y) :-
   parent(F,Y),
   father(X,F).

grandma(X,Y) :-
   parent(F,Y),
   mother(X,F).

现在上面的查询产生了想要的结果:

?- grandpa(m1,w2).
true.

?- grandma(w1,w3).
true.

最后,根据Cambridge Dictionary 的侄子是你的兄弟姐妹的儿子,或者你丈夫或妻子的兄弟姐妹的儿子。既然你没有丈夫和妻子的谓词,我会坚持你姐姐或兄弟的儿子部分。如果您为丈夫和妻子添加事实,您可以添加其他规则以涵盖定义的其他部分。您可以像这样在 Prolog 中编写定义的第一部分:

nephew(X,Y) :-
   man(X),
   dif(F,Y),
   parent(P,F),
   parent(P,Y),
   parent(F,X).

如果您查询此谓词,则不再有 erick/alberto 解决方案:

?- nephew(erick,X).
X = jaime ;
X = manolo ;
X = jaime ;
X = manolo ;
false.

【讨论】:

  • 也许我是老派,但我不明白为什么要在标准内置函数起作用时提出 dif/2 ......对于更简单的 Prologs 的用户,愿意学习基础知识呢?
  • @CapelliC:我认为 dif/2 比替代方案更易于使用,尤其是对于新用户。考虑以下查询:?- A\=B. 产生false.,而?- dif(A,B). 产生dif(A, B).,即传播约束。因此?- A\=B, A=a, B=b. 产生false.?- A=a, B=b, A\=B. 成功并产生A = a,B = b.。另一方面?- A=a, B=b, dif(A,B).?- dif(A,B), A=a, B=b. 都产生A = a,B = b.。因此,有必要确保...
  • @CapelliC: ...包含 \=/2 的目标是在其两个参数都被接地后发布的,因为即使?- A=a, A\=B, B=b. 也会产生false.。这可能会导致新用户很难发现的错误。我很清楚 dif/2 并非在所有 Prolog 实现中都可用,但在许多实现中都可用。而且我当然不反对使用标准内置插件,我只是发现 dif/2 是更简单/更优雅的选择,所以我建议它:-)
  • @CapelliC:我同意学习目标排序是必不可少的。但它的某些方面比其他方面更容易掌握。我发现直观容易掌握的一个方面是,某些目标可能会比其他目标更缩小搜索空间,并且将这些目标放在首位可能会导致更快的谓词。但是,知道目标通过连词连接并且连词是可交换的,我发现 A & B & C 可能产生与 B & C & A 不同的结果是非常违反直觉的。这就是为什么我认为 dif/2 对初学者来说可能更容易.
  • @tas:“导致更快的谓词” - 至少始终将终止目标放在首位不会恶化终止属性。也就是说,它们要么保持不变,要么变得更好。 Speedwise,这一切都取决于
【解决方案2】:

Prolog 这一切都是关于关系。连接起着基础性作用。因此,您通常可以考虑“产量”或数据库上的功能,并通过连接(子句,即逻辑公式)产生记录来设计/控制数据访问计划 - 就像 SQL 一样。检索数据的过程执行以(几乎)相同的语言表示:连接,让我们体验声明式编程。无论如何,这是@tas在标准Prolog中提供的子句:

brother(X,Y) :-
   man(X),
   parent(F, X),
   parent(F, Y),
   X\=Y.

sister(X,Y) :-
   woman(X),
   parent(P, X),
   parent(P, Y),
   X\=Y.

【讨论】:

  • 定义不正确,如果你把X\=Y 连一个目标放在前面。如果您使用iso_dif(X,Y),这不会发生!它发出一个干净的错误。因此,即使在标准 Prolog 中,您也可以编写正确且健壮的程序!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 2021-10-22
  • 2017-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多