【问题标题】:How to get values existing in two lists如何获取两个列表中存在的值
【发布时间】:2014-10-14 18:32:43
【问题描述】:

我有两个包含歌曲名称和歌手的列表——欧盟前 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]

不管怎样,有没有更优雅的方法呢?

【问题讨论】:

  • top10eu(L1), top10us(L2), findall(Song, (member(Song, L1), member(Song, L2)), Songs) 怎么样?
  • 还有 intersect/3...
  • @DanielLyons:哇!我不知道 findall() 和 member()。它做得很棒,但是如何在没有艺术家的情况下获得歌曲名称?结果必须是[stay_With_Me, all_About_That_Bass]。您可以将此作为答案发布,以便我选择它吗?
  • @CapelliC:我正在使用 swi-prolog 解释器,并且有 intersection/3。与丹尼尔的评论相同的问题 - 我怎样才能得到歌曲名称?
  • 使用 findall,比交集更容易: ... findall(Song, (E = song(Song,_), member(E, L1), member(E, L2)), Songs)

标签: prolog


【解决方案1】:

借助库谓词很容易获得两个未排序列表之间的连接:

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 将绑定到歌曲(标题,作者)。

【讨论】:

    【解决方案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
      ) .
    

    【讨论】:

    • 我接受 CapelliC 的回答只是因为他昨晚第一个提出了完整的解决方案。感谢您的全面回答。
    【解决方案3】:

    您要查找的内容通常称为set intersection。查看相关问题“Intersection and union of 2 lists”。

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

    我在my answer 中针对上述问题 OTOH 提供的代码是逻辑纯单调。因此,即使在使用非基本术语时,它在逻辑上仍然是合理的。

    【讨论】:

      猜你喜欢
      • 2020-04-08
      • 1970-01-01
      • 2016-04-06
      • 1970-01-01
      • 1970-01-01
      • 2014-09-14
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多