Пересечение и объединение 2 списков

Я начинаю изучать пролог (я использую SWI-пролог), и я выполнил простое упражнение, в котором у меня есть 2 списка, и я хочу вычислить их пересечение и объединение. Вот мой код, который работает довольно хорошо, но я спрашивал себя, есть ли лучший способ сделать это, так как я не люблю использовать оператор CUT.

intersectionTR(_, [], []).
intersectionTR([], _, []).
intersectionTR([H1|T1], L2, [H1|L]):-
    member(H1, L2),
    intersectionTR(T1, L2, L), !.
intersectionTR([_|T1], L2, L):-
    intersectionTR(T1, L2, L).

intersection(L1, L2):-
    intersectionTR(L1, L2, L),
    write(L).


unionTR([], [], []).
unionTR([], [H2|T2], [H2|L]):-
    intersectionTR(T2, L, Res),
    Res = [],
    unionTR([], T2, L),
    !.
unionTR([], [_|T2], L):-
    unionTR([], T2, L),
    !.

unionTR([H1|T1], L2, L):-
    intersectionTR([H1], L, Res),
    Res \= [],
    unionTR(T1, L2, L).
unionTR([H1|T1], L2, [H1|L]):-
    unionTR(T1, L2, L).

union(L1, L2):-
    unionTR(L1, L2, L),
    write(L).

Имейте в виду, что я хочу получить только 1 результат, а не несколько результатов (даже если они правильные), поэтому запустите код с этим:

?- intersect([1,3,5,2,4] ,[6,1,2]).

должен выйти с:

[1,2]
true.

а не с

[1,2]
true ;
[1,2]
true ;
etc...

То же самое должно быть действительным для предиката объединения.
Как я уже сказал, мой код работает довольно хорошо, но, пожалуйста, предложите лучшие способы сделать это.
Спасибо

8 ответов

Решение

Кроме того, не уверен, почему вы мертвы против сокращений, до тех пор, пока их удаление не изменит переводное значение кода согласно вашей ссылке. Например:

inter([], _, []).

inter([H1|T1], L2, [H1|Res]) :-
    member(H1, L2),
    inter(T1, L2, Res).

inter([_|T1], L2, Res) :-
    inter(T1, L2, Res).

test(X):-
        inter([1,3,5,2,4], [6,1,2], X), !.

test(X).
X = [1, 2].

В тестовом бите, где я вызываю код, я просто говорю, сделайте пересечение, но меня интересует только первый ответ. Нет никаких сокращений в самих определениях предикатов.

Следующее основано на моем предыдущем ответе " Удалить дубликаты в списке" (Пролог); основная идея, в свою очередь, основана на ответе @false профсоюзу Prolog для AUBUC.

Какое сообщение я хочу донести до вас?

  • Вы можете описать, что вы хотите в Прологе с логической чистотой.
  • С помощью if_/3 а также (=)/3 логически чистая реализация может быть
    • оба эффективны (оставляя позади точки выбора только при необходимости)
    • и монотонный (логически обоснованный в отношении обобщения / специализации).
  • Реализация предикатов @false if_/3 а также (=)/3 действительно использует мета-логические функции Пролога внутренне, но (извне) ведет себя логически чисто.

Следующая реализация list_list_intersection/3 а также list_list_union/3 использования list_item_isMember/3 а также list_item_subtracted/3, определенный в предыдущем ответе:

list_list_union([],Bs,Bs).
list_list_union([A|As],Bs1,[A|Cs]) :-
    list_item_subtracted(Bs1,A,Bs),
    list_list_union(As,Bs,Cs).

list_list_intersection([],_,[]).
list_list_intersection([A|As],Bs,Cs1) :-
    if_(list_item_isMember(Bs,A), Cs1 = [A|Cs], Cs1 = Cs),
    list_list_intersection(As,Bs,Cs).

Вот запрос, который вы разместили как часть вашего вопроса:

?- list_list_intersection([1,3,5,2,4],[6,1,2],Intersection).
Intersection = [1, 2].                    % succeeds deterministically

Давайте попробуем что-нибудь еще... Следующие два запроса должны быть логически эквивалентны:

?- A=1,B=3, list_list_intersection([1,3,5,2,4],[A,B],Intersection).
A = 1,
B = 3,
Intersection = [1, 3].
?- list_list_intersection([1,3,5,2,4],[A,B],Intersection),A=1,B=3.
A = 1,
B = 3,
Intersection = [1, 3] ;
false.

И... суть в том, что?

  • С чистым кодом легко оставаться на стороне логической обоснованности.
  • С другой стороны, нечистый код чаще всего действует с первого взгляда как "он делает то, что должен", но демонстрирует все виды нелогичного поведения с запросами, подобными приведенным выше.

Редактировать 2015-04-23

ни list_list_union(As,Bs,Cs) ни list_list_intersection(As,Bs,Cs) гарантировать, что Cs не содержит дубликатов. Если это вас беспокоит, код необходимо адаптировать.

Вот еще несколько запросов (и ответов) с As и / или Bs содержащие дубликаты:

?- list_list_intersection([1,3,5,7,1,3,5,7],[1,2,3,1,2,3],Cs).
Cs = [1, 3, 1, 3].
?- list_list_intersection([1,2,3],[1,1,1,1],Cs).
Cs = [1].

?- list_list_union([1,3,5,1,3,5],[1,2,3,1,2,3],Cs).
Cs = [1, 3, 5, 1, 3, 5, 2, 2]. 
?- list_list_union([1,2,3],[1,1,1,1],Cs).
Cs = [1, 2, 3].
?- list_list_union([1,1,1,1],[1,2,3],Cs).
Cs = [1, 1, 1, 1, 2, 3].

Редактировать 2015-04-24

Для полноты картины, вот как мы могли бы обеспечить, чтобы пересечение и объединение были множествами, то есть списками, которые не содержат повторяющихся элементов.

Следующий код довольно прост:

list_list_intersectionSet([],_,[]).
list_list_intersectionSet([A|As1],Bs,Cs1) :-
    if_(list_item_isMember(Bs,A), Cs1 = [A|Cs], Cs1 = Cs),
    list_item_subtracted(As1,A,As),
    list_list_intersectionSet(As,Bs,Cs).

list_list_unionSet([],Bs1,Bs) :-
    list_setB(Bs1,Bs).
list_list_unionSet([A|As1],Bs1,[A|Cs]) :-
    list_item_subtracted(As1,A,As),
    list_item_subtracted(Bs1,A,Bs),
    list_list_unionSet(As,Bs,Cs).

Обратите внимание, что list_list_unionSet/3 основывается на list_setB/2, определенный здесь.

Теперь давайте посмотрим, как list_list_intersectionSet/3 а также list_list_unionSet/3 В бою:

?- list_list_unionSet([1,2,3,1,2,3,3,2,1],[4,5,6,2,7,7,7],Xs).
Xs = [1, 2, 3, 4, 5, 6, 7].

?- list_list_intersectionSet([1,2,3,1,2,3,3,2,1],[4,5,6,2,7,7,7],Xs).
Xs = [2].

Чтобы обмануть чуть меньше моего первого ответа, вы можете использовать предикат findall высшего порядка, который заставляет Prolog выполнить рекурсию за вас:

4 ?- L1=[1,3,5,2,4], L2=[6,1,2], findall(X, (nth0(N, L1, X), member(X, L2)), Res).
L1 = [1, 3, 5, 2, 4],
L2 = [6, 1, 2],
Res = [1, 2].

Если цель состоит в том, чтобы просто "выполнить работу", то swi prolog встроил примитивы именно для этой цели:

[trace] 3 ?- intersection([1,3,5,2,4] ,[6,1,2], X).
intersection([1,3,5,2,4] ,[6,1,2], X).
X = [1, 2].

[trace] 4 ?- union([1,3,5,2,4] ,[6,1,2], X).
X = [3, 5, 4, 6, 1, 2].

Попробуйте это, аналог union/3 здесь:

      :- use_module(library(clpfd)).

member(_, [], 0).
member(X, [Y|Z], B) :-
   (X #= Y) #\/ C #<==> B,
   member(X, Z, C).

intersect([], _, []).
intersect([X|Y], Z, T) :-
   freeze(B, (B==1 -> T=[X|R]; T=R)),
   member(X, Z, B),
   intersect(Y, Z, R).

Это работает, если элементы являются целыми и не оставляют точки выбора:

      ?- intersect([X,Y],[Y,Z],L).
freeze(_15070,  (_15070==1->L=[X, Y];L=[Y])),
_15070 in 0..1,
_15166#\/_15168#<==>_15070,
_15166 in 0..1,
X#=Y#<==>_15166,
X#=Z#<==>_15168,
Y#=Z#<==>_15258,
_15168 in 0..1,
_15258 in 0..1.

?- intersect([X,Y],[Y,Z],L), X=1, Y=2, Z=3.
X = 1,
Y = 2,
Z = 3,
L = [2].

?- intersect([X,Y],[Y,Z],L), X=3, Y=2, Z=3.
X = Z, Z = 3,
Y = 2,
L = [3, 2].

Я знаю, что этот пост очень старый, но я нашел решение с минимальным кодированием.

% пересечение пересечение ([],L1,L2,L3). Пересечение ([H|T],L2,L3,[H|L4]):- член (Н,L2), пересечение (Т,L3,L3,L4). % член (H,[H|T]). Член (Х,[H|T]):- Член (X, Т).

Для проверки вышеуказанного кода не следует вводить L3. Вот примеры.

? - пересечение ([w,4,g,0,v,45,6],[x,45,d,w,30,0],L). L = [w, 0, 45].

И наконец (действительно), вы можете использовать findall, чтобы найти все решения, затем использовать nth0, чтобы извлечь первое, которое даст вам желаемый результат без сокращений, и сохранит предикаты красивыми и чистыми, без каких-либо дополнительных предикатов для ловушка / стоп пролог делает то, что умеет лучше всего - откатывает назад и находит несколько ответов.

Редактировать: Можно утверждать, что добавление дополнительных предикатов в "основную логику" для предотвращения создания нескольких результатов столь же безобразно / запутанно, как и использование сокращений, которых вы пытаетесь избежать. Но, возможно, это академическое упражнение, чтобы доказать, что это можно сделать без использования предикатов более высокого порядка, таких как findall или встроенное пересечение / объединение.

inter([], _, []).

inter([H1|T1], L2, [H1|Res]) :-
    member(H1, L2),
    inter(T1, L2, Res).

inter([_|T1], L2, Res) :-
    inter(T1, L2, Res).

test(First):-
        findall(Ans, inter([1,3,5,2,4], [6,1,2], Ans), Ansl), 
        nth0(0, Ansl, First).

% Элемент X находится в списке?

Перт (X, [X | _]).

pert (X, [_ | L]): - pert (X, L).

% Союз двух списков

союз ([], L, L).

объединение ([ X | L1 ], L2, [ X | L3 ]):- \+pert(X, L2), объединение (L1, L2, L3).

объединение ([_ | L1], L2, L3): - объединение (L1, L2, L3).

% Пересечение двух списков

Интер ([], _, []).

inter ([X | L1], L2, [X | L3]): - pert (X, L2), inter (L1, L2, L3).

inter ([_ | L1], L2, L3): - inter (L1, L2, L3).

Другие вопросы по тегам