簡體   English   中英

2個列表的交集和並集

[英]Intersection and union of 2 lists

我正在開始學習 prolog(我使用 SWI-prolog)並且我做了一個簡單的練習,其中我有 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...

對於聯合謂詞,同樣必須有效。
正如我所說,我的代碼運行良好,但請提出更好的方法。
謝謝

另外,不確定您為什么反對削減,只要根據您的鏈接,刪除它們不會改變代碼的聲明性含義。 例如:

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].

在我調用代碼的測試位中,我只是說做交集,但我只對第一個答案感興趣。 謂詞定義本身沒有任何削減。

以下內容基於我之前Remove duplicates in list (Prolog) 的回答 反過來,基本思想是基於@false 對 AUBUC 的 Prolog union的回答

我想向你傳達什么信息?

  • 您可以在 Prolog 中以邏輯純潔的方式描述您想要的內容。
  • 使用if_/3(=)/3邏輯純實現可以是
    • 既高效(僅在需要時留下選擇點)
    • 和單調(關於泛化/專業化在邏輯上是合理的)。
  • if_/3謂詞if_/3(=)/3內部確實使用了元邏輯 Prolog 功能,但(從外部)表現出邏輯純

list_list_intersection/3list_list_union/3的以下實現使用了list_item_isMember/3list_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/3list_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].

編輯 2019-01-30

這是從@GuyCoder 的評論中提取的附加查詢(加上它的兩個變體):

?- list_list_unionSet(Xs,[],[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b]
...

?- list_list_unionSet([],Xs,[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b]
...

?- list_list_unionSet(Xs,Ys,[a,b]).
   Xs = [], Ys = [a,b]
;  Xs = [], Ys = [a,b,b]
;  Xs = [], Ys = [a,b,b,b]
...

使用舊版本的list_item_subtracted/3 ,上述查詢並沒有終止存在。

有了新的,他們做到了。 由於解決方案集的大小是無限的,因此這些查詢都不會普遍終止。

為了比我的第一個答案略少作弊,您可以使用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].

最后(真的),您可以使用 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).

我知道這篇文章很舊,但我找到了一個編碼最少的解決方案。

% intersection
intersection([],L1,L2,L3).
intersection([H|T],L2,L3,[H|L4]):-member(H,L2),intersection(T,L3,L3,L4).
% member
member(H,[H|T]).
member(X,[H|T]):-member(X,T).

要測試上述代碼,您不應輸入 L3。 這是一個例子。

?- intersection([w,4,g,0,v,45,6],[x,45,d,w,30,0],L).
L = [w, 0, 45].

% 元素 X 在列表中?

pert(X, [ X | _ ])。

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

% 兩個列表的並集

聯合([ ],L,L)。

union([ X | L1 ], L2, [ X | L3 ]):- \\+pert(X, L2), union(L1, L2, L3)。

union([ _ | L1 ], L2, L3):- union(L1, L2, L3)。

% 兩個列表的交集

間([ ],_,[ ])。

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

間([ _ | L1 ],L2,L3):-間(L1,L2,L3)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM