如何获取两个列表中存在的值

时间:2014-10-14 18:32:43

标签: prolog

我有两个歌曲名单和歌手名单 - 欧盟前10名,美国前10名。结果应该是一个包含两个列表中存在的歌曲的列表。

以下是设置列表的谓词:

top10us([
    song(all_About_That_Bass, "Meghan Trainor"),
    song(shake_It_Off, "Taylor Swift"),
    song(black_Widow, "Iggy Azalea Featuring Rita Ora"),
    song(bang_Bang, "Jessie J, Ariana Grande & Nicki Minaj"),
    song(anaconda, "Nicki Minaj"),
    song(habits, "Tove Lo"),
    song(dont_Tell_Em, "Jeremih Featuring YG"),
    song(animals, "Maroon 5"),
    song(stay_With_Me, "Sam Smith"),
    song(break_Free, "Ariana Grande Featuring Zedd")
]).

top10eu([
    song(prayer_In_C, "Lilly Wood & Prick"),
    song(lovers_On_The_Sun, "David Guetta & Sam Martin"),
    song(chandelier, "Sia"),
    song(rude, "Magic!"),
    song(stay_With_Me, "Sam Smith"),
    song(maps, "Maroon 5"),
    song(all_Of_Me, "John Legend"),
    song(all_About_That_Bass, "Meghan Trainor"),
    song(a_Sky_Full_Of_Stars, "Coldplay"),
    song(bailando, "Enrique Iglesias")
]).

以下是我尝试编写谓词的两次尝试。

尝试1:

member(ITEM,[song(ITEM,_)|_],1).
member(_,[],0).
member(ITEM,[_|T],R):-
    member(ITEM,T,R).

both([],[],[]).
both([song(NAME,_)|T1], L2,[NAME|T3]):-
    member(NAME,L2,R),R=1,both(T1, L2, T3).
both([_|T], L2, T3):-
    both(T, L2, T3).

?- top10eu(L1),top10us(L2),both(L1,L2,RESULT),write(RESULT). // the result is false here

尝试2:

both(_,[],[],[]).
both(ORIGINAL,[_|T],[],OUTPUT):-
    both(ORIGINAL,T,ORIGINAL,OUTPUT).
both(ORIGINAL, [song(NAME,SINGER)|T1], [song(NAME,_)|T2],[NAME|T3]):-
    both(ORIGINAL, [song(NAME,SINGER)|T1], T2, T3).
both(ORIGINAL, L1, [_|T2], T3):-
    both(ORIGINAL, L1, T2, T3).
?- top10eu(L1),top10us(L2),both(L1,L1,L2,RESULT),write(RESULT). // here a list is returned, but with wrong elements.

我把头埋在墙上好几个小时,无法解决这个问题。有人可以进入prolog看看吗?我认为这不是太具体,因为主要问题是如何在两个列表中获取值。

编辑:我的第二次尝试正好工作,但我用错误的参数列表调用它。使用?- top10eu(L1),top10us(L2),both(L2,L1,L2,RESULT),write(RESULT).调用它会返回正确的列表:RESULT = [stay_With_Me, all_About_That_Bass]

无论如何,有更优雅的方式吗?

3 个答案:

答案 0 :(得分:2)

在库谓词的帮助下,可以轻松获得两个未排序列表之间的连接:

both(L1,L2,L) :-
 findall(E, (member(E, L1), memberchk(E, L2)), L).

只提取部分匹配结构,或执行更精细的匹配,我们可以优化目标:

bothSong(L1,L2,L) :-
 findall(Song, (E = song(Song, _), member(E, L1), memberchk(E, L2)), L).
请注意,只会检索具有相同作者的歌曲。这是因为变量E将绑定到歌曲(标题,作者)。

答案 1 :(得分:2)

要找到交叉点的两个列表,你可以说出这样的话:

intersection( []     , _  , []     ) .  % once the source list is exhausted, we're done.
intersection( [X|Xs] , Ys , [X|Zs] ) :- % otherwise, add X to the results...
  member(X,Ys)                          % - if X is found in Y (built-in predicate)
  ! ,                                   % - cut off alternatives
  intersection(Xs,Ys,Zs)                % - recurse down
  .                                     %
intersection( [_|Xs] , Ys , Zs     ) :- % otherwise (X not in Y), discard X
   intersection(Xs,Ys,Zs)               % - and recurse down
   .                                    % Easy!

如果您不允许使用内置的member/2,那么它可能非常简单,只需滚动自己:

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

但是从您的帖子来看,您可能希望对这些数据结构进行一些重构,并将其作为一堆事实:

top10( us , all_About_That_Bass   , "Meghan Trainor"                        ).
top10( us , shake_It_Off          , "Taylor Swift"                          ).
top10( us , black_Widow           , "Iggy Azalea Featuring Rita Ora"        ).
top10( us , bang_Bang             , "Jessie J, Ariana Grande & Nicki Minaj" ).
top10( us , anaconda              , "Nicki Minaj"                           ).
top10( us , habits                , "Tove Lo"                               ).
top10( us , dont_Tell_Em          , "Jeremih Featuring YG"                  ).
top10( us , animals               , "Maroon 5"                              ).
top10( us , stay_With_Me          , "Sam Smith"                             ).
top10( us , break_Free            , "Ariana Grande Featuring Zedd"          ).
top10( eu , prayer_In_C            , "Lilly Wood & Prick"                   ).
top10( eu , lovers_On_The_Sun      , "David Guetta & Sam Martin"            ).
top10( eu , chandelier             , "Sia"                                  ).
top10( eu , rude                   ,  "Magic!"                              ).
top10( eu , stay_With_Me           , "Sam Smith"                            ).
top10( eu , maps                   , "Maroon 5"                             ).
top10( eu , all_Of_Me              , "John Legend"                          ).
top10( eu , all_About_That_Bass    , "Meghan Trainor"                       ).
top10( eu , a_Sky_Full_Of_Stars    , "Coldplay"                             ).
top10( eu , bailando               , "Enrique Iglesias"                     ).

然后你可以说:

both( Country1 , Country2 , Common ) :-
  findall(
    song(Title,Performer) ,
    ( top10(Country1,Title,Performer) ,
      top10(Country2,Title,Performer)
    ) ,
    Common
  ) .

如果你只想要这首歌的名字:

both( Country1 , Country2 , Common ) :-
  findall(
    Title ,
    ( top10(Country1,Title,_) ,
      top10(Country2,Title,_)
    ) ,
    Common
  ) .

答案 2 :(得分:2)

您所寻找的通常称为set intersection。查看相关问题" Intersection and union of 2 lists"。

其他答案中显示的所有代码逻辑上不纯。 逻辑上不纯的代码经常在以与实现者意图略有不同的方式使用时中断。

我在my answer中向上述问题提出的代码OTOH 逻辑纯单调。因此,即使使用非基础术语,它仍然保持逻辑上的声音。