我对Prolog很新,我正在尝试制作一个isIntersection
,它给我两个列表的交集,并将答案放在第三个列表中。我不能使用任何Prolog列表谓词,因为它适用于一个类,而且只是规则。这就是我所拥有的,我在调试和查看此实现错误的原因时遇到了问题。有人有什么想法吗?
/* Checks if the item is in the list */
in(Item, [Item|Rest]).
in(Item, [Not|Rest]) :- in(Item, Rest).
/* Makes the intersection list */
isIntersection([], [], []).
isIntersection([H|R], List, [H|Final]) :-
in(H, List),
isIntersection(R, List, Final),
write(H).
isIntersection([Discard|Rest], List, Final) :-
isIntersection(Rest, List, Final),
write(Discard).
答案 0 :(得分:2)
Prolog是一种非常通用的查询语言,所以让我们使用Prolog来找到问题!
?- isIntersection([a,b],[c,b],Zs).
false.
这种失败不是我们所期望的。为了更好地本地化问题,我们可以a)概括查询或b)减少输入大小。我将首先尝试概括它:
?- isIntersection([a,b],Ys,Zs).
ERROR: Out of global stack
似乎我们没有运气,但是此查询必须为Ys
生成无限多个列表,因此可能可以循环。
我可以继续这样,但为什么不让Prolog为我做这个想法呢?我会尝试所有可能的列表:
?- length(L,_),append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
L = Xs, Xs = Ys, Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685, _G688],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685, _G688, _G691],
Ys = Zs, Zs = [] ...
因此,对于每个列表长度(到目前为止),只有一个解决方案Ys
和Zs
是一个空列表... Ys
是否有更大的解决方案?
?- length(L,_),Ys = [_|_], append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
LOOPS
因此,让我们从上面得到最小的缺失示例,Ys
有一个元素:
?- isIntersection([],[a],[]).
false.
有了这个目标,现在看看你的代码!
但还有另一个问题(在上面修复之后):
?- isIntersection([a],[a],Xs).
Xs = [a] ;
Xs = [].
该规则会丢弃任何元素!但它应该只丢弃那些不在List
中的那些。所以:
isIntersection([Discard|Rest], List, Final) :-
list_without(List,Discard), % maplist(dif(Discard),List)
isIntersection(Rest, List, Final).
list_without([], _).
list_without([E|Es], F) :-
dif(E, F),
list_without(Es, F).
最后,请始终关注否定示例。此处的许多尝试(错误地)都会成功执行isIntersection([a],[a],[])
等查询。
(您的关系in/2
最好称为element_in/2
)
答案 1 :(得分:0)
只有List
可以匹配您的基本情况,而这个简单的fact
会禁止整个计算。
答案 2 :(得分:0)
我会这样做,排序和合并以避免O(n 2 )表现:
intersection_of( Xs , Ys , Zs ) :- % to find the intersection of two sets, we
sort(Xs,X1) , % - sort the left source list, removing duplicates to ensure that it's a set
sort(Ys,Y1) , % - sort the right source list, removing duplicates to ensure that it's a set
merge(Xs,Ys,Z1) , % - merge them to find the common members (an ordered set)
( var(Zs) -> % - if the result is unbound,
Zs = Z1 ; % - simply unify the merge result with the result set
sort(Zs,Z1) % - otherwise, sort the result and match against the merge result
) . %
合并很简单
merge( [] , [] , [] ) .
merge( [_|_] , [] , [] ) .
merge( [] , [_|_] , [] ) .
merge( [X|Xs] , [Y|Ys] , [Z|Zs] ) :- X = Y , merge( Xs , Ys , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X @< Y , merge( Xs , [Y|Ys] , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X @> Y , merge([X|Xs] , Ys , Zs ) .