如果我要确保两个变量都不会实例化为同一术语,那么首选的处理方式是什么?
假设我需要在图中找到有向边,并且节点本身不能有边:
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
表示两个术语是不同的。