如果我要确保两个变量都不会实例化为同一术语,那么首选的处理方式是什么?

假设我需要在图中找到有向边,并且节点本身不能有边:

node(a, x, y). node(b, z, x). node(c, y, y).


(这里的边是a-> c,b-> a,但不是c-> c)

以下作品:

edge(A, B) :- node(A, _, X), node(B, X, _), A \== B.


这也可以[swi-prolog]:

edge(A, B) :- dif(A, B), node(A, _, X), node(B, X, _).


显然,这不起作用(因为A和B都没有实例化吗?):

edge(A, B) :- A \== B, node(A, _, X), node(B, X, _).


我想第一个解决方案的问题在于,使用更复杂的node谓词,在edge失败之前,可能会发生许多不必要的统一。另一方面,dif在库中,这表明它不打算在如此简单的情况下使用(尽管它具有我似乎正在寻找的确切功能)。

最佳答案

仅出于优雅和教学上的原因,在这里以及在大多数其他情况下,dif/2显然是更可取的,因为正如您已经注意到的那样,否则“很多不必要的统一可能会发生”,并且因为dif/2是纯粹的和很好的声明性谓词,与(\==)/2相比,它可以在子句主体的所有方向和任何位置使用,而无需更改程序的含义。 dif/2也是SWI-Prolog中的自动加载谓词,这意味着您无需显式导入任何库即可使用它,并且dif/2与任何内置谓词一样可用。

如果使用dif/2,则可以更轻松地推断代码。例如,在您的情况下,您从以下内容开始:

边缘(A,B):-节点(A,_,X),节点(B,X,_),dif(A,B)。


然后,您知道dif/2是一个完全纯的谓词,因此您也可以这样写:

edge(A,B):-dif(A,B),node(A,_,X),node(B,X,_)


此外,由于您知道dif/2总是终止,因此您知道此更改最多可以改善程序的终止属性。

像所有约束一样,必须使用dif/2。我强烈建议您使用它,而不要使用非可交换谓词。

如果您担心性能,可以做一个较小的比较,只是在两个谓词可以互换使用的用例中,将dif/2与非声明性(\==)/2进行比较:

?-N = 1_000_000,时间((((1,N,_),dif(a,b),false))。
%11,000,005推断,0.353 CPU,0.353秒(100%CPU,31281029 Lips)

?-N = 1_000_000,时间((((1,N,_),a \ == b,false))。
%@%3,000,001推论,0.107 CPU,0.107秒(99%CPU,28167437 Lips)


因此,使用(\==)/2有时会带来性能上的好处。但是,使用这样的低级谓词时,也存在许多更严重的缺点:较难理解,更容易出错,而且没有声明性。

因此,我建议仅使用dif/2表示两个术语是不同的。

10-06 10:47