“\+;”的问题在带有变量的Prolog查询中

“\+;”的问题在带有变量的Prolog查询中,prolog,predicate,gnu-prolog,Prolog,Predicate,Gnu Prolog,我正在读“七周七种语言”atm,我被一些我不理解“否”回答的序言问题难住了 friends.pl文件如下所示: likes(wallace, cheese). likes(grommit, cheese). likes(wendolene, sheep). friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z). 我可以对它进行一些琐碎的查询,例如: | ?- ['friends']. compiling /home/marc/btlang-c

我正在读“七周七种语言”atm,我被一些我不理解“否”回答的序言问题难住了

friends.pl
文件如下所示:

likes(wallace, cheese).
likes(grommit, cheese).
likes(wendolene, sheep).

friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
我可以对它进行一些琐碎的查询,例如:

| ?- ['friends'].
compiling /home/marc/btlang-code/code/prolog/friends.pl for byte code...
/home/marc/btlang-code/code/prolog/friends.pl compiled, 12 lines read - 994 bytes written, 8 ms

yes
| ?- friend(wallace,grommit).

yes
| ?- friend(wallace,wendolene).

no
这一切都是意料之中的。现在,我想在查询中引入一个变量。我的意图是Prolog会给我一份华莱士所有朋友的名单。我期待着
X=grommit
,但我得到了

| ?- trace.
The debugger will first creep -- showing everything (trace)

yes
{trace}
| ?- friend(wallace,X).
      1    1  Call: friend(wallace,_16) ?
      2    2  Call: \+wallace=_16 ?
      3    3  Call: wallace=_16 ?
      3    3  Exit: wallace=wallace ?
      2    2  Fail: \+wallace=_16 ?
      1    1  Fail: friend(wallace,_16) ?

no
{trace}

它甚至没有尝试将
X
\u16
)与
grommit
统一起来。为什么?

朋友/2的第一个子目标是
\+(X=Y)
。这是通过首先尝试为
X=Y
找到解决方案,然后否定该结果来执行的。谓词
=/2
大致相当于
unify/2
,即它尝试将左操作数与右操作数统一起来。现在,当您使用例如
friend(wallace,gromit)
来询问问题时,两个原子
wallace
gromit
并不统一;但是,当一个自由变量被投入到混合中时,它总是与给定的任何术语相一致,因此
X=Y
总是成功的,因此
\+(X=Y)
总是失败,并且执行永远不会超过第一个子目标。

这是friend的定义:

friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
这里重要的一点是从
\+(X=Y)
开始,通常定义为:

\+ Goal :- Goal,!,fail
请注意,这意味着如果目标成功,您肯定会失败。自由变量(未分配的变量)总是统一的,因此是相等的,所以你总是会因为一个自由变量而失败。因此,如果X或Y没有值,它将永远不会为X或Y赋值

反而

friend(X, Y) :-  likes(X, Z), likes(Y, Z), \+(X = Y)
会表现得更像你期望的那样


这里的问题是,prolog为您提供了强大的方法来控制程序流,但这些方法并不真正适合其更面向逻辑的设计。应该可以用不产生这些问题的方式来表示“否定为失败”类型的约束。出于这个原因,我不是一个超级序言迷

关于Philip JF的上述评论:

应该可以表达 “否定为失败”类型约束 以一种不会产生这些的方式 问题


这是可能的:这些问题的现代解决方案是约束。在这种情况下,使用所有严肃的Prolog系统中可用的dif/2作为示例。

首先使用不等式约束的另一个问题是:无法为未绑定的
X
找到绑定(目前不包括将其与grommit统一的琐碎情况)。Prolog通过运行其数据库来查找绑定,并尝试统一未绑定的变量。这就是为什么
likes(grommit,Z)
会找到
Z
的一些绑定,然后可以进一步处理,因为数据库中有
likes
子句。但是对于grommit与某物的不等式没有这样的子句,因此Prolog不能产生任何绑定。目前情况下,
friend
谓词必须确保所有变量都已绑定,然后才能测试不等式。

好的,明白了。你将如何重新编写
friend/2
,使其能够处理包含变量的查询?乍一看,我会使用等式运算符
=/2
,并将其重写为
friend(X,Y):-\+(X==Y)、likes(X,Z)、likes(Y,Z)。
但是这个版本有一个缺陷,就是将你自己包含在朋友之中,例如
friend>(华莱士,X)
将华莱士列为解决方案。因此,正如Philip JF所建议的那样,即使他仍在使用统一运算符,最好将检查结果放在末尾:
朋友(X,Y):-喜欢(X,Z),喜欢(Y,Z),X\==Y
。(请注意,
\=/2
相当于否定
=/2
,但更清晰、更直接。)
gprolog
(v1.3):
未捕获异常:错误(存在错误(过程,dif/2),朋友/2)
尝试例如SWI、Yap、SICStus、B-Prolog和其他几个。