为什么 Prolog 的否定失败不被视为逻辑否定?

问题描述 投票:0回答:3

在许多 Prolog 指南中,以下代码用于说明 Prolog 中的“失败否定”。

not(Goal) :- call(Goal), !, fail. 
not(Goal).

但是,这些相同的教程和文本警告说,这不是“逻辑否定”。

问题:有什么区别?

我尝试进一步阅读这些文本,但他们没有详细说明其中的区别。

prolog logic negation negation-as-failure
3个回答
5
投票

我喜欢@TesselationHeckler 的回答,因为它指出了问题的核心。您可能仍然想知道,这对于 Prolog 来说更具体意味着什么。考虑一个简单的谓词定义:

p(something).

从根本上来说,我们得到了查询的预期答案:

?- p(something).
true.

?- \+ p(something).
false.

?- p(nothing).
false.

?- \+ p(nothing).
true.

当变量和替换发挥作用时,问题就开始了:

?- \+ p(X).
false.

p(X)
并不总是假的,因为
p(something)
是真的。到目前为止,一切都很好。让我们用等式来表达替换,并检查是否可以这样导出
\+ p(nothing)

?- X = nothing, \+ p(X).
X = nothing.

在逻辑上,目标的顺序并不重要。但是当我们想要导出重新排序的版本时,它失败了:

?- \+ p(X), X = nothing.
false.

X = nothing, \+ p(X)
的区别在于,当我们到达那里的否定时,我们已经统一了
X
,这样 Prolog 就会尝试导出我们知道为真的
\+p(nothing)
。但在另一个顺序中,第一个目标是更一般的
\+ p(X)
,我们看到它是错误的,让整个查询失败。

这当然不应该发生——在最坏的情况下,我们会期望不终止,但永远不会失败而不是成功。

因此,我们不能再依赖对子句的逻辑解释,而必须在涉及否定时立即考虑 Prolog 的执行策略。


5
投票

有几个原因,

实例化不足

目标

not(Goal_0)
将会失败,当且仅当执行此
Goal0
not/1
在时间点
成功。因此,它的含义取决于执行该目标时恰好出现的实例。改变目标的顺序可能会改变
not/1
的结果。所以合取是不可交换的。

有时这个问题可以通过重新编写实际查询来解决。

防止错误答案的另一种方法是检查目标是否“充分实例化”,通过检查“

ground(Goal_0)”是否为真,否则会产生实例化错误。这种方法的缺点是经常会产生实例化错误,而人们不喜欢它们。

甚至还有一种方法是适当延迟
Goal_0

的执行。提高这种方法的粒度的技术称为“构造性否定”。您会发现很多关于它的出版物,但它们还没有进入通用 Prolog 库。原因之一是,当存在许多延迟目标时,此类程序特别难以调试。

当将 Prolog 的否定与约束结合起来时,事情会变得更糟。想想 
X#>Y,Y#>X 没有解决方案,但 not/1

只看到了它的成功(即使成功是有条件的)。

语义歧义
如果普遍否定,Prolog 认为存在“恰好一个最小模型”的观点不再成立。只要只考虑分层程序,这就不是问题。但是有许多程序没有分层但仍然是正确的,例如实现否定的元解释器。一般情况下有几个最小模型。解决这个问题远远超出了 Prolog 的范围。

学习 Prolog 时,首先

坚持纯粹、单调的部分。这部分比许多人预期的要丰富得多。无论如何你都需要掌握这一部分。

逻辑主张:“有一只黑天鹅”。


5
投票
  • 找到黑天鹅是对黑天鹅存在的有力支持。

  • 逻辑否定:“不存在黑天鹅”。

Prolog 否定:“

我还没有发现
    黑天鹅”。
  • 这并不能有力地支持这一主张。 “你看过吗?你看过那边吗?”逻辑版本在任何地方都没有黑天鹅的空间,Prolog 版本的代码质量可能很差,搜索整个地球到天鹅大小区域的计算限制,停止搜索之前花费多长时间的时间限制;它为代码未找到的黑天鹅留下了空间。最坏的情况是周围都有黑天鹅,但代码中的错误意味着它没有在任何地方进行搜索。
  • 逻辑否定不需要任何人去寻找任何地方,该主张独立于任何证据或反驳。 Prolog 逻辑纠结于 Prolog 可以使用你编写的代码证明什么,不能证明什么。

© www.soinside.com 2019 - 2024. All rights reserved.