序言存在时不给我解决方案

prolog doesn't give me a solution when one exists

我正在 七周学习七种语言,但我对 prolog 有一些不了解的地方。我有以下程序(基于他们的 wallace 和 grommit 程序):

/* teams.pl */

onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).

teamMate(X, Y) :- \+(X = Y), onTeam(X, Z), onTeam(Y, Z).

并像这样加载它

?- ['teams.pl'].
true.

但它没有给我任何解决以下问题的方法

?- teamMate(a, X).
false.

它可以解决更简单的问题(如书中所示):

?- onTeam(b, X).
X = aTeam ;
X = superTeam.

还有解决办法:

?- teamMate(a, b).
true ;
false.

我错过了什么?我已经尝试过 gnu prolog 和 swipl。

...还有更多...

当您将 "can't be your own teammate" 限制移动到结束时:

/* teams.pl */

onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).

teamMate(X, Y) :- onTeam(X, Z), onTeam(Y, Z), \+(X = Y).

它给了我期望的解决方案:

?- ['teams.pl'].
true.

?- teamMate(a, X).
X = b.

?- teamMate(b, X).
X = a ;
X = c.

什么给了?

你观察得很好!事实上,情况更糟,因为即使 最一般的 查询 也会失败 :

?- teamMate(X, Y).
false.

声明地,这意味着 "there are no solutions whatsoever",这显然是 错误的 而不是我们期望关系的行为方式:如果有解决方案,那么 更多一般 查询不得失败

您出现这种奇怪且逻辑上不正确的行为的原因是 (\+)/1 只有在其参数 充分实例化 时才是正确的。

要以更通用的方式表达术语的不质量,它可以正常工作无论参数是否被实例化,使用dif/2,或者,如果您的 Prolog 系统不提供它,您可以在 标签。

例如,在您的情况下(note_that_I_am_using_underscores_for_readability 而不是 tuckingTheNamesTogetherWhichMakesThemHarderToRead):

team_mate(X, Y) :- dif(X, Y), on_team(X, Z), on_team(Y, Z).

您的查询现在完全符合预期:

?- team_mate(a, X).
X = b.

最一般的查询当然也能正常工作:

?- team_mate(X, Y).
X = a,
Y = b ;
X = b,
Y = a ;
X = b,
Y = c ;
etc.

因此,使用dif/2来表达不平等保留你们的关系:系统现在不再简单地说false即使有解决方案。相反,您会得到您期望的答案!请注意,与之前相比,这同样有效无论您在哪里拨打电话

为您提供了一些高级注意事项和解决方案。我的回答更多是关于根本原因,您可能感兴趣也可能不感兴趣。

(顺便说一下,在学习 Prolog 时,我问了同一个用户很多 the same question and got a very similar answer。太好了。)

证明树

您有一个问题:

Are two players team mates?

要从 Prolog 获得答案,您需要制定一个查询:

?- team_mate(X, Y).

其中 X 和 Y 都可以是自由变量或绑定变量。

根据您的谓词数据库(事实和规则),Prolog 会尝试找到证明 并为您提供解决方案。序言 searches for a proof by doing a depth-first traversal of a proof tree.

在您的第一个实现中,\+ (X = Y) 出现在其他任何东西之前,因此它位于树的根节点,并且将在以下目标之前进行评估。如果 XY 是自由变量,则 X = Y 必须成功 ,这意味着 \+ (X = Y) 必须失败。所以查询一定会失败.

另一方面,如果 XY 是自由变量,dif(X, Y) 将成功 ,但稍后的尝试将它们彼此统一 必须失败 。到那时,Prolog 将不得不在证明树的另一个分支下寻找证明,如果还有的话。

(考虑到证明树,尝试找出一种实现 dif/2 的方法:你认为没有任何一个 a)添加某种状态到 [=19= 的参数中是可能的吗? ] 或 b) 更改解析策略?)

最后,如果你把 \+ (X = Y) 放在最后,并注意 XY 在计算时都被磨碎了,那么统一就变成了更像是一个简单的比较,它可以失败,所以否定可以成功