[英]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的回答。
我想向你传达什么信息?
if_/3
和(=)/3
逻辑纯实现可以是
if_/3
谓词if_/3
和(=)/3
在内部确实使用了元逻辑 Prolog 功能,但(从外部)表现出逻辑纯。 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.
而且……底线是?
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].
为了完整起见,下面是我们如何强制交集和并集是集合——即不包含任何重复元素的列表。
以下代码非常简单:
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].
这是从@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.