Использование ==/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