我正在阅读“7周内七种语言”的内容,我对Prolog的一些问题感到困惑,因为我不理解“不”的回应。
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
,但我得到no
:
| ?- 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
(_16
)与grommit
统一起来。为什么?
答案 0 :(得分:4)
这是朋友的定义:
friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
重要的是你从\+(X = Y)
开始,通常定义为:
\+ Goal :- Goal,!,fail
请注意,这意味着如果目标成功,您肯定会失败。自由变量(未分配的变量)将始终统一,因此相等,因此您将始终使用自由变量失败。因此,如果它还没有值,它将永远不会为X或Y赋值。
相反
friend(X, Y) :- likes(X, Z), likes(Y, Z), \+(X = Y)
会表现得像你期望的那样。
这里的问题是prolog为您提供了强大的方法来控制程序的流程,但那些不太适合其更多的逻辑导向设计。应该可以以不产生这些问题的方式表达“否定为失败”类型约束。由于这个原因,我不是一个巨大的序幕粉丝。
答案 1 :(得分:4)
关于Philip JF上面的评论:
应该可以表达 “否定为失败”类型约束 以一种不产生这些的方式 问题。
这是可能的:这些问题的现代解决方案是限制。在这种情况下,使用例如dif/2
,在所有严肃的Prolog系统中都可用。
答案 2 :(得分:3)
friend/2
的第一个子目标是\+(X = Y)
。这是通过首先尝试找到X = Y
的解决方案,然后否定该结果来执行的。谓词=/2
大致等同于unify/2
,即它尝试将左操作数与右操作数统一。现在,当您使用例如询问查询时friend(wallace, gromit)
,两个原子wallace
和gromit
不统一;但是当一个自由变量被抛入混合中时,它总是与给定的任何术语统一,因此X = Y
总是成功的,因此\+(X = Y)
总是失败,并且执行永远不会超过第一个子目标。< / p>
答案 3 :(得分:1)
首先使用不等式约束的另一个问题是:找到未绑定X
的绑定是不可行的(不包括暂时将其与grommit统一的简单情况)。 Prolog通过运行其数据库找到绑定,尝试统一未绑定的变量。这就是为什么likes(grommit, Z)
会找到Z
的一些绑定然后可以进一步处理的原因,因为数据库中有likes
个子句。但是对于某些事物的不平等没有这样的条款,所以Prolog不能产生任何约束力。事实上,friend
谓词必须确保在测试不等式之前绑定所有变量。