使用\\ == / 2或DIF / 2(Using \\==/2 or dif/2)

2019-06-21 18:06发布

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

比方说,我需要在图形找到有向边,节点不能有边本身:

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

(这里的边缘是一个- > C,B - >一个,但不是 C - > c)中

以下工作:

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

这工作太[SWI-序言]:

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谓词,很多不必要的unifications的可能之前发生edge失败。 该dif ,另一方面是在图书馆,这表明它并不意味着在这种简单的情况下使用(虽然它,我似乎在寻找精确的功能)。

Answer 1:

对于单独的优雅和教学的原因, dif/2显然是最好这里也是在绝大多数的其他情况下,因为你已经注意到“很多不必要的unifications的可能发生”,否则,也因为dif/2是一个纯粹的并很好地声明谓词可以在所有方向和在身体条款的任何地方使用,而没有改变所述程序的意义,而相比之下, (\==)/2dif/2也是SWI-Prolog的自动装载断言,这意味着你不需要任何导入库明确地使用它, dif/2可以像任何内置谓词。

如果您使用的dif/2 ,你可以更容易地推理代码。 例如,你的情况,你开始:

edge(A, B) :- node(A, _, X), node(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, 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)

所以,有时有使用时的性能优势(\==)/2 。 然而,使用这种低层次的谓语时,也有更严重的缺陷:这是很难理解的,更容易出错,而不是声明。

因此,我建议只使用dif/2来表达两个方面是不同的。



Answer 2:

查询是间解释和开销可能超过的差异dif(X,Y)X\==Y 。 你应该比较这两个谓词:

t1:-
    1000=I,
    time(t1(I)).


t1(I):-
    dif(X,Y), 
    between(1,I,X), 
    between(1,I,Y), 
    false.

t2:-
    1000=I,
    time(t2(I)).


t2(I):-
    between(1,I,X), 
    between(1,I,Y), 
    X\==Y,
    false.

在B-Prolog的,我得到了以下结果:

| ?- cl(t)
Compiling::t.pl
compiled in 0 milliseconds
loading::t.out

yes
| ?- t1
CPU time 0.14 seconds.
no
| ?- t2
CPU time 0.078 seconds.
no
| ?- 1000=I,time(( dif(X,Y), between(1,I,X), between(1,I,Y), false )).
CPU time 0.234 seconds.
no
| ?- 1000=I,time(( between(1,I,X), between(1,I,Y), X \== Y, false )).
CPU time 0.218 seconds.


Answer 3:

首先, dif/2(\==)/2意味着同样当两个参数被研磨,这是可变的自由。 所以,如果你能保证参数将是地面-或者更确切地说,充分实例化,从而进一步实例不会影响结果(\==)/2 -那么它不会有所作为。

在你的榜样,我们需要确切知道答案node/3总是包含地面的第一个参数。 在这种情况下, (\==)/2程序是好的。 在极少数情况下,它可能会比低效率的dif/2版本。 认为目标的edge(X, X)

在许多情况下, (\==)/2或甚至(\=)/2是显著更有效。 在另一方面,相对于正确性时,是多么重要的效率?

看到此的另一种方式,是考虑(\==)/2(\=)/2为近似值来自两个方面:只有都同意,我们是否有一个安全的最终结果。

从历史上看, dif/2是最古老的内置谓词之一。 这是存在于第一个的Prolog系统,其有时被称为Prolog的0到从中经常被认为是第一Prolog的下一个版本区分开来-马赛Prolog的-的Prolog 1. Prolog的1并不再有dif/2和它在这种形状的Prolog来到爱丁堡。 另外, dif/2不是(目前)ISO标准的一部分,因为它需要一些coroutining状机构。 和许多(而不是旧的)Prolog的系统没有这样的机制。 然而,即使在ISO Prolog的人可以做的更好:

iso_dif(X, Y) :-
   X == Y,
   !,
   fail.
iso_dif(X, Y) :-
   X \= Y,
   !.
iso_dif(X, Y) :-
   throw(error(instantiation_error,iso_dif/2)).

(这里是另一个,可能是更有效的实现 )

注意有问题的情况下,如何通过停止整个计算错误覆盖。

支持当前的Prolog系统dif/2开箱的是B,SICStus,SWI,YAP。 正是在IF,侨,XSB库。

另见这个答案 。


为了支持我的有关费用要求,这里是在同一台机器上的各种Prologs测试。 在SWI,有10倍的开销,在B,没有开销。 如已通过@nfz指出,编译东西的时候数字略有不同。 所以,你的里程可能会有所不同。

SWI 6.3.4-55

?- 1000=I,time(( dif(X,Y), between(1,I,X), between(1,I,Y), false )).
% 22,999,020 inferences, 5.162 CPU in 5.192 seconds (99% CPU, 4455477 Lips)
false.

?- 1000=I,time(( between(1,I,X), between(1,I,Y), X \== Y, false )).
% 2,000,001 inferences, 0.511 CPU in 0.521 seconds (98% CPU, 3912566 Lips)
false.

乙7.8

| ?- 1000=I,time(( dif(X,Y), between(1,I,X), between(1,I,Y), false )).
CPU time 0.364 seconds.
no
| ?- 1000=I,time(( between(1,I,X), between(1,I,Y), X \== Y, false )).   
CPU time 0.356 seconds.
no

MAKE

?- 1000=I,time(( dif(X,Y), between(1,I,X), between(1,I,Y), false )).
% 2.528 CPU in 2.566 seconds ( 98% CPU)
no
?- 1000=I,time(( between(1,I,X), between(1,I,Y), X \== Y, false )).
% 0.929 CPU in 0.963 seconds ( 96% CPU)
no


文章来源: Using \\==/2 or dif/2