问题描述
如果我想确保两个变量不会实例化为同一个术语,那么首选的方法是什么?
假设我需要在图中找到有向边,而一个节点自身不能有边:
节点(a,x,y).节点(b,z,x).节点(c,y,y).
(这里的边是a -> c,b -> a,但不是 c -> c)
以下作品:
edge(A, B) :- 节点(A, _, X), 节点(B, X, _), A == B.
这也有效 [swi-prolog]:
edge(A, B) :- diff(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
,您可以更轻松地推断您的代码.例如,在你的情况下,你开始:
然后,你知道 dif/2
是一个完全纯谓词,你知道你也可以这样写:
此外,由于您知道 dif/2
总是终止,因此您知道此更改最多可以改善程序的终止属性.
与所有约束一样,dif/2
是要使用的.我强烈推荐它而不是不可交换的不纯谓词.
如果您担心性能,这里有一个小比较,只是将 dif/2
与非声明性 (==)/2
进行比较两个谓词可以互换使用的用例:
因此,使用 (==)/2
有时会带来性能优势.然而,使用这种低级谓词时也有更严重的缺点:更难理解、更容易出错,而且不是声明性的.
因此我建议简单地使用 dif/2
来表示两个术语不同.
If I want to make sure that two variables do not instantiate to the same term, what is the preferred way to do it?
Let's say I need to find directed edges in a graph, and a node cannot have an edge to itself:
node(a, x, y). node(b, z, x). node(c, y, y).
(the edges here are a -> c, b -> a, but not c -> c)
The following works:
edge(A, B) :- node(A, _, X), node(B, X, _), A == B.
This works too [swi-prolog]:
edge(A, B) :- dif(A, B), node(A, _, X), node(B, X, _).
This does not work, apparently (because neither A nor B are instantiated yet?):
edge(A, B) :- A == B, node(A, _, X), node(B, X, _).
I guess my problem with the first solution is that, with a more complex node
predicate, a lot of unnecessary unifications might take place before edge
fails. The dif
on the other hand is in a library, which suggests that it is not meant to be used in such a simple case (although it has the exact function that I seem to be looking for).
For elegance and didactic reasons alone, dif/2
is clearly preferable here and also in the vast majority of other cases, since as you already note "a lot of unnecessary unifications might take place" otherwise, and also because dif/2
is a pure and nicely declarative predicate that can be used in all directions and at any place in the clause body without changing the meaning of the program, in contrast to (==)/2
. dif/2
is also an autoloaded predicate in SWI-Prolog, meaning that you need not import any library explicitly to use it, and dif/2
is available like any built-in predicate.
If you use dif/2
you can reason much more easily about your code. For example, in your case, you start with:
edge(A, B) :- node(A, _, X), node(B, X, _), dif(A, B).
and then, as you know that dif/2
is a completely pure predicate, you know that you can also write this as:
edge(A, B) :- dif(A, B), node(A, _, X), node(B, X, _).
Further, since you know that dif/2
always terminates, you know that this change can at most improve the termination properties of your program.
Like all constraints, dif/2
is meant to be used. I highly recommend it instead of impure predicates that are not commutative.
In case you are worried about performance, here is a small comparison, just comparing dif/2
against the non-declarative (==)/2
in a use case where the two predicates can be used interchangeably:
?- N = 1_000_000, time((between(1,N,_),dif(a,b),false)). % 11,000,005 inferences, 0.352 CPU in 0.353 seconds (100% CPU, 31281029 Lips) ?- N = 1_000_000, time((between(1,N,_),a==b,false)). %@ % 3,000,001 inferences, 0.107 CPU in 0.107 seconds (99% CPU, 28167437 Lips)
So, there are sometimes performance benefits when using (==)/2
. However, there are also much more severe drawbacks when using such a low-level predicate: It is harder to understand, more error-prone, and not declarative.
I therefore recommend to simply use dif/2
to express that two terms are different.
这篇关于使用 ==/2 或 diff/2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!