使用\ ==/2或dif/2

16 prolog prolog-dif logical-purity

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

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

node(a, x, y). node(b, z, x). node(c, y, y).
Run Code Online (Sandbox Code Playgroud)

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

以下作品:

edge(A, B) :- node(A, _, X), node(B, X, _), A \== B.
Run Code Online (Sandbox Code Playgroud)

这也有效[swi-prolog]:

edge(A, B) :- dif(A, B), node(A, _, X), node(B, X, _).
Run Code Online (Sandbox Code Playgroud)

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

edge(A, B) :- A \== B, node(A, _, X), node(B, X, _).
Run Code Online (Sandbox Code Playgroud)

我想我的第一个解决方案的问题是,使用更复杂的node谓词,在edge失败之前可能会发生许多不必要的统一.在dif另一方面,是在图书馆,这表明它并不意味着在这种简单的情况下使用(虽然它,我似乎在寻找精确的功能).

mat*_*mat 14

仅仅为了优雅和教学原因,dif/2在这里以及在绝大多数其他情况下显然是可取的,因为你已经注意到"可能会发生许多不必要的统一",而且因为它dif/2是一个纯粹且很好的声明性谓词,它可以在不改变程序含义的情况下,可以在所有方向和子句中的任何地方使用,与之相反(\==)/2.dif/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用来表达两个术语不同.

  • `? - X\== Y,X = Y.成功,但是`? - X = Y,X\== Y`失败.从纯逻辑关系来看,我们期望目标的顺序不会改变连词的真实性. (4认同)
  • "从纯粹的逻辑关系来看,我们希望目标的顺序不会改变连词的真实性." 这是我所有困惑的根源.在你的帮助下,它已经消失了.再次感谢您抽出宝贵时间! (3认同)
  • 谢谢您的回答.在仔细查看SWI-Prolog手册的"Coroutining"部分之后,它是有道理的.我仍然有点困惑的是,在仅使用内置谓词时,这样一个简单的Prolog程序实际上需要程序性考虑. (2认同)

小智 8

该查询是元解释和开销可能超过的差异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.
Run Code Online (Sandbox Code Playgroud)

在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.
Run Code Online (Sandbox Code Playgroud)


fal*_*lse 7

首先,当两个参数都是基础时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)).
Run Code Online (Sandbox Code Playgroud)

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

请注意有问题的情况如何被一个停止整个计算的错误所覆盖.

目前支持dif/2开箱即用的Prolog系统是B,SICStus,SWI,YAP.它位于IF,Ciao,XSB的库中.

另见这个答案.


为了支持我对开销的主张,这里是在同一台机器上的各种Prolog中的测试.在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.

B 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

YAP

?- 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