Prolog中切割算子的交换性

Commutativity of Cut Operator in Prolog

我目前正在学习 Prolog,在我正在阅读的其中一篇笔记中给出了如何正确使用 cut 运算符的示例。考虑以下函数从列表中删除特定值的所有元素。

rm(_,[],[]).
rm(A,[A|L],R) :- rm(A,L,R).
rm(A,[B|L],[B|R]) :- rm(A,L,R).

由于回溯,这不是函数的正确定义,函数将 return 从删除 a 的 some 元素中获得的列表的所有子列表特定值,但不一定全部。我正在阅读的笔记说解决这个问题的正确方法是用行

替换第二行
rm(A,[A|L],R) :- !, rm(A,L,R)

但是将行替换为

rm(A,[A|L],R) :- rm(A,L,R), !

不正确。我不确定为什么第二个示例是修复该功能的错误方法。在 swipl 中,用这些修复替换第二项似乎总是 return 与我考虑的测试用例的相同答案。我在这里错过了什么?

你的例子是一个完美的例子,可以说明为什么在此处使用剪切从来都不是一个好主意。

仅当第一个和第二个参数都充分实例化时,使用rm(A,[A|L],R) :- !, rm(A,L,R).才有意义。但是如果它们没有充分实例化,你会得到一个不完整的答案,比如:

?- rm(X, [a], R).
   X = a, R = [].       % incomplete

这显然错过了答案,因为它将 X 限制为仅 a。但是如果 X 是其他任何东西,我们会得到不同的结果,即:

?- X = b, rm(X,[a],R).
R = [a].

在最后使用 rm(A,[A|L],R) :- rm(A,L,R), !. 中的 cut 更糟糕:首先,到目前为止我们所有的假设都必须成立,然后 此外 第三个参数不能被实例化。否则我们会得到更多不正确的解决方案。

?- rm(a,[a],R).
R = [].

?- rm(a,[a],[a]).
true                 % incorrect

回想一下我们在这里问的是什么:

User: When removing a from the list [a] what do we get?

Prolog: Nothing, nil, nada.

User: But can't I have instead of nothing just [a]? Please!

Prolog: OK, I give in.

这不是您想要实施会计系统的方式。

所以削减的两种用法都是不好的。但第二个显然更糟,因为它需要记住更多的前提条件,而且效率低下。 另一方面,在某些情况下您可以使用这些谓词。但通常很难记住什么时候是安全的。因此,这样的削减是错误的永久来源。

有希望摆脱所有这些细则吗?幸运的是,有一种方法可以使用 if_/3 from library(reif) for SICStus|SWI。下载并说:

:- use_module(reif).

rm(_,[],[]).
rm(A,[X|Xs], Ys0) :-
   if_(A = X, Ys0 = Ys, Ys0 = [X|Ys]),
   rm(A, Xs, Ys).

这个程序比较高效,但没有任何上述缺陷:

?- rm(X, [a], R).
   X = a,
   R = []
;  R = [a],
   dif(X, a).

注意第二个新答案!它说对于所有不同于 aX,列表保持不变。