Prolog 变量查询中的“\+”问题
我正在读《七周七种语言》atm,我对一些 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
。为什么?
I'm reading "Seven languages in seven weeks" atm, and I'm stumped over some Prolog query that I don't understand the 'no' response to.
The friends.pl
file looks like this:
likes(wallace, cheese).
likes(grommit, cheese).
likes(wendolene, sheep).
friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
I can do some trivial queries on it, such as:
| ?- ['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
This is all as expected. Now, I want to introduce a variable in the query. My intent being that Prolog will give me a list of all of Wallace's friends. I'm expecting X = grommit
, but I'm getting 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}
It doesn't even try to unify X
(_16
) with grommit
. Why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是朋友的定义:
这里重要的是你以
\+(X = Y)
开头,通常定义为:请注意,这意味着如果目标成功,你肯定会失败。自由变量(尚未分配的变量)将始终统一,因此是相等的,因此自由变量总是会失败。因此,如果 X 或 Y 还没有值,它永远不会分配值。
相反,他们的
行为会更加符合你的预期。
这里的问题是 prolog 为您提供了控制程序流程的强大方法,但这些方法并不太适合其更面向逻辑的设计。应该可以以不会产生这些问题的方式表达“否定为失败”类型的约束。由于这个原因,我并不是 prolog 的忠实粉丝。
It is the definition of friend:
The important thing here is that you start with
\+(X = Y)
which is normally defined as:Note that this means that if goal succeeds, you are sure to fail. Free variables (ones that havent been assigned) will always unify, and thus be equal, so you will always fail with a free variable. It will thus never assign a value to X or Y if it doesn't already have one.
Instead
will behave more as you expect.
The problem here is that prolog gives you powerful ways to control the flow of programs, but those dont really fit nicely with its more logic oriented design. It should be possible to express "negation as failure" type constraints in a way that does not produce these problems. I'm not a huge prolog fan for this reason.
关于 Philip JF 的上述评论:
这是可能的:此类问题的现代解决方案是约束。在这种情况下,请使用例如
dif/2
,它在所有严肃的 Prolog 系统中都可用。Regarding Philip JF's comment above:
This is possible: The modern solution for such problems are constraints. In this case, use for example
dif/2
, available in all serious Prolog systems.friend/2
的第一个子目标是\+(X = Y)
。这是通过首先尝试找到X = Y
的解决方案,然后否定该结果来执行的。谓词=/2
大致相当于unify/2
,即它尝试将左操作数与右操作数统一。现在,当您使用例如friend(wallace, gromit)
进行查询时,wallace
和gromit
这两个原子不会统一;但是当一个自由变量被放入混合中时,它总是与给定的任何术语统一,因此X = Y
总是成功的,因此\+(X = Y)
总是失败,并且执行永远不会超过第一个子目标。The first subgoal of
friend/2
is\+(X = Y)
. This is executed by first trying to find a solution forX = Y
, then negating that result. The predicate=/2
is roughly the equivalent ofunify/2
, that is it tries to unify the left operand with the right operand. Now, when you are asking queries using e.g.friend(wallace, gromit)
, the two atomswallace
andgromit
do not unify; but when a free variable is thrown into the mix, it always unifies with whatever term is given, soX = Y
is always successful, therefore\+(X = Y)
always fails, and the execution never gets past that first subgoal.首先具有不等式约束的另一个问题是:无法找到未绑定的 X 的绑定(暂时排除将其与 grommit 统一的简单情况)。 Prolog 通过运行其数据库来查找绑定,尝试统一未绑定的变量。这就是为什么
likes(grommit, Z)
会找到一些Z
的绑定,然后可以进一步处理,因为在数据库。但是没有这样的子句来表示 gromit 与某些东西的不等式,因此 Prolog 无法产生任何绑定。按照目前的情况,friend
谓词必须确保所有变量都已绑定,然后才能测试不等式。Another issue with having the inequality constraint first is: It is uncapable to find a binding for the unbound
X
(excluding the trivial case of unifying it with grommit for the moment). Prolog finds bindings by running through its database, trying to unify the unbound variable. That is whylikes(grommit, Z)
will find some binding forZ
which can then be further processed, because there arelikes
clauses in the database. But there are no such clauses for the inequality of grommit with something, so Prolog cannot produce any bindings. As things stand, thefriend
predicate has to make sure that all variables are bound before the inequality can be tested.