使用 ==/2 或 diff/2 [英] Using ==/2 or dif/2

查看:25
本文介绍了使用 ==/2 或 diff/2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

假设我需要在图中找到有向边,而一个节点自身不能有边:

节点(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,您可以更轻松地推断您的代码.例如,在你的情况下,你开始:

<上一页>边缘(A,B):-节点(A,_,X),节点(B,X,_),dif(A,B).

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

<上一页>边缘(A,B):- dif(A,B),节点(A,_,X),节点(B,X,_).

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

与所有约束一样,dif/2 是要使用的.我强烈推荐它而不是不可交换的不纯谓词.

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

<上一页>?- N = 1_000_000,时间((介于(1,N,_),dif(a,b),假)).% 11,000,005 次推理,0.352 个 CPU,0.353 秒(100% CPU,31281029 个嘴唇)?- N = 1_000_000,时间((介于(1,N,_),a==b,假)).%@ % 3,000,001 次推理,0.107 CPU,0.107 秒(99% CPU,28167437 唇)

因此,使用 (==)/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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆