Using \==/2 or dif/2

前端 未结 3 1664
故里飘歌
故里飘歌 2020-11-27 08:18

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 grap

3条回答
  •  刺人心
    刺人心 (楼主)
    2020-11-27 09:03

    First of all, dif/2 and (\==)/2 mean the same when both arguments are ground, that is variable free. So if you can ensure that the arguments will be ground — or rather sufficiently instantiated such that further instantiations will not affect the outcome of (\==)/2 — then it does not make a difference.

    In your example, we would need to know for sure that answers for node/3 contain always a ground first argument. In that case, the (\==)/2 program is fine. In rare cases it might be less efficient than the dif/2 version. Think of the goal edge(X, X).

    In many situations, the (\==)/2 or even (\=)/2 is significantly more efficient. On the other hand, how important is efficiency when compared to correctness?

    Another way of seeing this, is to consider (\==)/2 and (\=)/2 as approximations from two sides: Only if both agree, do we have a safe final outcome.

    Historically, dif/2 is one of the oldest built-in predicates. It was present in the very first Prolog system which is sometimes called Prolog 0 to distinguish it from the next version which is often perceived to be the first Prolog — the Marseille Prolog — Prolog 1. Prolog 1 did no longer have dif/2 and it is in this shape that Prolog came to Edinburgh. Also,dif/2 is not part of the ISO standard (currently) since it requires some coroutining-like mechanism. And many (rather older) Prolog systems do not have such a mechanism. However, even in ISO Prolog one could do better:

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

    (Here is another, probably more efficient implementation)

    Note how the problematic cases are covered by an error that stops the entire computation.

    Current Prolog systems that support dif/2 right out of the box are B, SICStus, SWI, YAP. It is in a library of IF, Ciao, XSB, Scryer.

    See also this answer.


    To support my claim about the overheads, here is a test in various Prologs on the same machine. In SWI, there is an overhead of a factor of 10, in B, there is no overhead. As has been noted by @nfz, numbers are slightly different when compiling things. So your mileage may vary.

    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
    

提交回复
热议问题