使用列表谓词在Prolog中查找交集

时间:2014-06-16 02:26:29

标签: list prolog intersection

我对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).

3 个答案:

答案 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 = [] ...

因此,对于每个列表长度(到目前为止),只有一个解决方案YsZs是一个空列表... 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 ) .