Использование ==/2 или dif / 2

если я хочу убедиться, что две переменные не инстанцируют один и тот же термин, каков предпочтительный способ сделать это?

предположим, мне нужно найти направленные ребра в графе, и узел не может иметь ребра для себя:

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

(края здесь a - > c, b - > a, но не c - > c)

следующие работы:

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

это тоже работает [swi-prolog]:

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 предикат, много ненужных объединений может произойти до edge не удается. The dif С другой стороны, в библиотеке, что предполагает, что он не предназначен для использования в таком простом случае (хотя он имеет точную функцию, которую я, кажется, ищу).

3 ответов


только из элегантности и дидактических соображений,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 чтобы выразить, что два термина разные.


запросы мета-интерпретируются, и накладные расходы могут перевешивать различия 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.

прежде всего,dif/2 и (\==)/2 означает то же самое, когда оба аргумента заземлены, то есть переменная свободна. Поэтому, если вы можете гарантировать, что аргументы будут обоснованы - или, скорее, достаточно инстанцированы, чтобы дальнейшие инстанцирования не повлияли на результат (\==)/2 - тогда это не имеет значения.

в вашем примере нам нужно будет точно знать, что ответы на node/3 всегда содержать первый аргумент. В таком случае,(\==)/2 программа в порядке. В редких случаях это может быть менее эффективным, чем dif/2 версия. Подумайте о цели edge(X, X).

во многих ситуациях (\==)/2 или даже (\=)/2 значительно эффективнее. С другой стороны, насколько важна эффективность по сравнению с правильность?

другой способ увидеть это является рассмотреть (\==)/2 и (\=)/2 как приближения с двух сторон: только если оба согласны, у нас есть безопасный конечный результат.

исторически dif/2 один из старейших встроенных предикатов. Он присутствовал в самой первой системе пролога, которая иногда называется Пролог 0, чтобы отличить ее от следующей версии, которая часто воспринимается как первый Пролог - марсельский Пролог - Пролог 1. У пролога 1 больше не было dif/2 и именно в таком виде Пролог пришел в Эдинбург. Кроме того,dif/2 не является частью стандарта ISO (в настоящее время), поскольку он требует некоторого механизма, подобного coroutining. И многие (довольно старые) системы пролога не имеют такого механизм. Однако даже в 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, Ciao, XSB.

см. также этот ответ.


чтобы поддержать мое утверждение о накладных расходах, вот тест в различных Прологах на одной машине. В 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