20

鉴于以下事实和谓词:

sound(time1).
sound(time2).
sun(time3).
relax(X):-sound(X),!,sun(X).
relax(_):-sun(_).

执行时relax(S).,我希望得到S=time1由于!,即(如果我错了,请纠正我),如果满足 'X' ,则停止回溯。

这是跟踪:

3 ?- trace.
true.

[trace] 3 ?- relax(S).
   Call: (6) relax(_G1831) ? creep
   Call: (7) sound(_G1831) ? creep
   Exit: (7) sound(time1) ? creep
   Call: (7) sun(time1) ? creep
   Fail: (7) sun(time1) ? creep
   Fail: (6) relax(_G1831) ? creep
false.

那么为什么 Prolog 还要检查sun(time1),即使它在满足后遇到了感叹号sound(X)(因为sound(time1)是事实)。

4

3 回答 3

34

为了进一步澄清这一点,如果有人仍然对感叹号运算符的工作方式感到困惑(就像我所做的那样),这里有一个例子:

sound(time3).
sound(time1).
sun(time1).
relax(X):-sound(X),!,sun(X).

对于这个特定示例,如果您向 Prolog 询问?-relax(S).此结果为false。我们可以这样描述 Prolog 的工作方式:

  1. Prolog 搜索询问的谓词(在我们的示例中,relax(SOMEVARIABLE unifiable with S))。
  2. Prolog 找到谓词relax(X)。变量 X 和 S 现在已绑定。
  3. Prolog 开始评估子句:
    • 声音(X)
      • Prolog 在文件中搜索满足 sound(X) 的事实。
      • 它发现事实声音(time3)。并将其与我们的变量 X=S=time3 统一起来。
      • Prolog 继续到下一个子句,即 operator !所以他不会回溯到这个运营商的背后。
    • 太阳(X)
      • Prolog 在文件中搜索满足 sun(X) 的事实。X 已经绑定,因此它会搜索不存在的sun(time3) 。
  4. 结论
    • 在这一点上,如果没有!运算符 Prolog 将返回 ( backtrack ) 到 sound(X) 并将变量 X=S 重新分配为 X=S=time1。由于事实sun(time1)存在,这最终会导致true 。
    • Prolog 返回 false,因为他无法匹配任何规则的relax(S)。

反对,就像我在 4. 中所说的那样没有!操作它导致成功

sound(time3).
sound(time1).
sun(time1).
relax(X):-sound(X),sun(X).

如果我在某些时候错了,请随时纠正我。

于 2016-04-03T16:35:52.053 回答
29

!标志防止将其右侧的子句回溯到左侧,它就像一个单向门,因此它不会回溯到切口之外。

sound(time1)为真时,sun(time1)将评估下一个子句,只有这样 prolog 才会发现sun(time1)false(通过搜索知识库,它实际上并不知道这是一个事实)。

然后,由于剪切,prolog 不会在第一个子句中尝试值time2和。time3

更多关于切割:

Prolog 从左到右评估谓词的子句。它将一个值绑定到最左边子句中的一个变量。如果子句是true,则移动到下一个子句。如果是false,prolog 也会尝试其他值。

如果任何值不能满足任何子句,则它将是false,整个谓词也是如此(因为子句由 AND 连接)。

整个事情就像树的深度优先遍历一样,其中子句是节点,边代表其变量的不同值。如果遍历发现一个子句是false,它将返回到它的前一个子句并尝试不同的值。

剪裁来了。如果!在两个子句之间放置一个 cut ( ) ,则意味着如果cut之后的子句变为false,则仅当评估在 cut 之后运行时才会继续尝试新值。这意味着在切割之前使用的变量的值被锁定,并且当评估穿过切割时它们无法更改。

于 2013-02-25T11:23:31.713 回答
4

它仍然会尝试满足规则的其余部分,只是不会回溯到感叹号之前。也就是说,如果sun(X)失败,它不会回溯并尝试将不同的对象匹配到sound(X),但无法完全匹配该规则。

于 2013-02-25T11:23:22.437 回答