【问题标题】:Getting the shortest list from a list of lists从列表列表中获取最短列表
【发布时间】:2021-05-14 20:52:44
【问题描述】:

我正在处理一个项目,但我遇到了一个谓词问题,该谓词需要获取列表列表中所有列表的最短列表。假设我们有以下列表:

Listoflists = [[u,f,c,x,e], [a,v,c], [r,j,[m], [a,l,c,p]]

所以我想要的是一个谓词shortest/2,当被问到shortest(Listoflists, A) 时,它会以A = [m] 回答。问题是我不能使用 maplist/3 因为项目的限制,这就是为什么我问自己而不是使用别人的问题。 我已经看到 keysort/2 在这些情况下很有用,但为了使其工作,我需要一个带有对 (list, listlength) 的列表(或类似的东西,我'不确定),我不知道如何得到它。如果有任何帮助,我正在 Ciao Prolog 工作,感谢您的回答。

【问题讨论】:

    标签: prolog


    【解决方案1】:

    如果 library(aggregate) 不被禁止,那么一个简单的解决方案是

    shortest(L,S) :- aggregate(min(C,E),(member(E,L),length(E,C)),min(_,S)).
    

    library(aggregate) 功能强大,值得学习,但您的问题可以使用 setof/3 轻松解决。想试试吗?让我知道...

    编辑

    使用标准的内置 setof/3,代码类似,使用 member/2 访问每个 list' 元素并获取其长度,然后用于排序(在 setof/3 中)或最小化(在聚合/3 中)。

    shortest(L,S) :- setof((C,E),(member(E,L),length(E,C)),[(_,S)|_]).
    

    这适用于特定情况,但 library(aggregate) 更通用,所以我更喜欢之前显示...

    【讨论】:

    • 那么,如果我想使用这个库,我将如何声明我的模块?现在它是这样的::- module(_,_,[classic,assertions,regtypes]). 但如果我写:- module(_,_,[aggregate,classic,assertions,regtypes]). 会发生存在错误。对不起,如果它太明显了,但我在 Ciao Prolog 中有点菜鸟。感谢您的友好回答!
    • 哪个序言?最有可能的是,它没有库(聚合)...我将展示如何使用 setof/3...
    • 我不知道它是哪个序言,但它没有聚合,也不适用。但是你的最后一段代码对我有用!所以非常感谢你的努力:)
    【解决方案2】:

    您可以通过创建另一个谓词(我在这里称之为shorter)然后在shortest 中使用它来实现这一点:

    shorter(_, [], []) :- !.
    shorter([], _, []).
    shorter([A | R1], [_ | R2], [A | R1]) :- shorter(R1, R2, R1), !.
    shorter([_ | R1], [B | R2], [B | R2]) :- shorter(R1, R2, R2).
    
    shortest([], []).
    shortest([A], A) :- !.
    shortest([A, B | R], X) :- shorter(A, B, T), shortest([T | R], X).
    

    【讨论】:

      【解决方案3】:

      我们可以采取生成方法,

      %% L must be uninstantiated
      shortest( LS, L ) :-
        length( L, _),      % length of L is N=0 or N+1
        member( L, LS).     % L is a member of LS
      

      生成的第一个解决方案将是列表LS 中(最左边的)最短的解决方案。因此,如果需要,请添加剪辑!

      【讨论】:

      • 这很好,但是您应该按照 OP 的要求重新排序参数,否则会很混乱
      • 目标shortest(S, [[a,b,c,d], [e,f,g], [h,i], [j], [k,l,m,n,o]])) 没有得到最短的子列表,它只是按长度升序枚举给定列表的元素。此外,在生成更长的子列表后,执行会因 stackoverflow 错误而停止(因为目标 length(L,_) 对未实例化的 L 有无限的解决方案,每次都有更长的列表)。
      • @slago:是的,但是很容易获得正确的版本 wrt OP' 要求:shortest(LS,L) :- length(L,_),member(L,LS),!.
      • @CapelliC 啊,你是对的,错过了!谢谢。
      【解决方案4】:

      这样的事情对你有帮助:

      shortest( [A], A ). % lists of length 1 are easy
      shortest( [A,B|C], S ) :-
          shortest( [B|C], D ) ,
          shorter( A, D, S ).
      
      shorter( A , B, C ) :-
          length(A,La),
          length(B,Lb),
          (   La < Lb ->  C = A ; C = B ).
      

      但它会留下选择点悬而未决,计算量大(每次调用都要遍历两个列表),而且它会在调用堆栈上推送很多东西:给定足够长的列表,你会得到堆栈溢出。

      不过,进行一些重构,我们可以对此进行改进。下面没有留下开放的选择点,它只计算每个子列表的长度一次,它是尾递归的,因此不会增加堆栈。

      shortest( [A     ] , A ) .
      shortest( [A,B|Cs] , S ) :-
          length(A,La),
          shortest( [B|Cs], La:A, S ).
      
      shortest( []     , _:S  , S ).
      shortest( [A|Bs] , Lc:C , S ) :-
          length(A,La),
          ( La < Lc ->  X = La:A ; X = Lc:C ),
          shortest(Bs,X,S).
      

      【讨论】:

        【解决方案5】:

        另一种可能的解决方案是:

        shortest([L], L).
        shortest([L1,L2|R], S) :- min(L1, L2, L), shortest([L|R], S).
        
        min(L1, L2, S) :- min(L1, L2, L1, L2, S).
        
        min([], _, L1, _, L1).
        min(_, [], _, L2, L2).
        min([_|R1], [_|R2], L1, L2, S) :- min(R1, R2, L1, L2, S).
        

        这种方案的优点是:

        • 它避免了使用条件构造(IF -&gt; THEN ; ELSE)(或cut)。

        • 它避免了多次比较两个列表以获得具有最小长度的列表(独立于参数的顺序)的需要。

        • 它避免了按长度对列表进行排序的需要。

        • 它避免了使用特殊库的需要。

        • 当有多个具有最小长度的列表时,它的工作方式不确定。

        这是一个例子:

        ?- shortest([[a,b,c,d], [e,f,g], [h,i], [j], [k], [l,m,n,o]],S).
        S = [j] ;
        S = [k] ;
        false.
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-21
          • 2019-02-13
          • 2019-12-31
          相关资源
          最近更新 更多