1

我想知道Prolog如何解决这个程序:

test(X, Y).
test(X, X):-!, fail.

我用谷歌搜索了“否定失败”,但我很困惑!

4

2 回答 2

12

考虑以下示例:

father(nick, john).

我们使用谓词father(X,Y)来表示X的父亲是Y。让我们查询数据库:

?- father(nick,X).
X = john.

?- father(john,Y).
false.

在这两种情况下,我们都询问谁是某人的父亲(分别是尼克、约翰)。在第一种情况下,prolog 知道答案 (john),但在第二种情况下它不知道,所以答案是错误的,这意味着 john 没有任何父亲。我们可能会期望,因为我们没有给 prolog 提供有关 john 父亲的任何信息,它会以unknown. 那将是一个开放的世界,如果不知道某些事情,我们不会认为它是错误的。相反,在 prolog 的封闭世界中,如果我们不知道某件事,我们就认为它是错误的。

请注意,我们说我们不知道约翰的父亲是谁,基于知道任何人都必须有父亲的世界不是一个开放的世界;它可以很容易地在 prolog 中建模:

data_father(nick, john).
father(X,Y):-
    data_father(X,Y) -> true ; true.

另一方面,在开放世界序言中,您将编写事实和反事实:

father(nick, john).
not father(adam, X).

这就是失败的否定。但是,这不是您的程序中发生的情况:

test(X, Y).
test(X, X):-!, fail.

无论参数的值如何,第一个子句总是会成功。事实上,正因为如此,给参数命名是没有意义的,prolog 会给你一个单例警告;您可以将子句写为test(_, _).

另一方面,第二个子句总是会失败。它可能以两种方式失败:(1)参数可能不同(2)参数是统一的,因此 prolog 移动到主体然后失败。

正是因为 prolog 使用的是封闭世界模型,所以没有任何意义的子句(没有副作用(但无论如何这被认为是不好的做法))总是失败。相反,这些额外的调用会导致您的程序运行速度变慢并使用更多内存。

还值得注意的是 cut ( !/0) 在这里什么都不做,因为当你到达它时,没有更多的选择点。但是考虑这个例子:

test(X, Y).
test(X, X):-!, fail.
test(X, 42).

?- test(1,42).
true ;
true.

?- test(42,42).
true ;
false.

在这两种情况下,prolog 都会创建 3 个选择点,每个子句一个。在第一种情况下,Prolog 将成功匹配第一个子句的头部并成功,因为没有主体。然后,它将无法匹配第二个子句的头部,并且主体不会被“执行”。最后,它将匹配第三个子句的头部并成功,因为没有主体。

但是,在第二种情况下:Prolog 将成功匹配第一个子句的头部并且成功,因为没有主体。然后,它将成功匹配第二个子句的头部;剪切将删除所有其他选择点,然后由于fail. 因此,prolog 不会尝试第三个子句。

自从你提到它以来,关于否定失败的几句话。作为失败的否定是基于封闭世界的假设;因为我们假设任何不能从我们已有的事实中推断出来的东西都是错误的,如果我们不能证明某件事,就意味着它的反面被认为是正确的。例如,考虑一下:

father(nick, john).
fatherless(X) :- \+ father(X, _).

?- fatherless(nick).
false.

?- fatherless(john).
true.

相反,在具有以下代码的开放世界序言中:

father(nick, john).
not father(adam, X).
fatherless(X) :- \+ father(X, _).

fatherless/1只会为adam_unknown

于 2012-12-29T10:24:06.280 回答
1

第一个子句test(X, Y).说 test/2 对于任何参数模式都是无条件的。

第二个子句test(X, X):-!, fail.说,当使用统一的第一个和第二个参数调用 test/2 时,没有更多的选择,然后失败(请注意,总是会失败,因为参数模式正在排除第一个参数 \= 第二个隐含的实例化模式)。

在“封闭世界假设”下,如果与逻辑否定相同,则操作效果。

于 2012-12-29T08:10:59.793 回答