Мы начнем с чистой реализации, основанной на встроенном предикате member/2
:
common_member(Xs,Ys) :-
member(E,Xs),
member(E,Ys).
Примеры запросов:
?- common_member([1,2,3],[1]).
true
; false.
?- common_member([1,2,3],[4]).
false.
?- common_member([1,2,3],[2,3,1]).
true
; true
; true
; false.
Декларативно, приведенный выше код в порядке.
Тем не менее, он оставляет за собой бесполезные точки выбора при успехе.
Кроме того, мы получаем избыточные ответы, если в обоих списках присутствует более одного элемента.
Можем ли мы улучшить вышеуказанные аспекты эффективности, оставаясь логически чистыми?
Да!
Но как? Используя if_/3
вместе с утвержденным тестовым предикатом memberd_t/3
!
common_memberd([X|Xs],Ys) :-
if_(memberd_t(X,Ys), true, common_memberd(Xs,Ys)).
Давайте снова запустим примеры запросов, на этот раз с common_memberd/2
:
?- common_memberd([1,2,3],[1]).
true.
?- common_memberd([1,2,3],[4]).
false.
?- common_memberd([1,2,3],[2,3,1]).
true.
Избыточные ответы были исключены, а последующие запросы делают это детерминировано.
Обратите внимание, что common_memberd/2
чисто, поэтому мы получаем надежные ответы даже для довольно общих запросов!
?- common_memberd([1,2,3],[A,B]).
A=1
; dif(A,1), B=1
; A=2 , dif(B,1)
; dif(A,1), dif(A,2), B=2
; A=3 , dif(B,1), dif(B,2)
; dif(A,1), dif(A,2), dif(A,3), B=3
; false.