从列表列表中获取最短列表

Getting the shortest list from a list of lists

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

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 工作,谢谢您的回答。

您可以通过创建另一个谓词(我在这里称之为 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).

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

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

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

编辑

使用标准内置setof/3,代码类似,使用member/2访问每个列表元素并获取其长度,然后用于排序(在setof/3 ) 或最小化 (in aggregate/3).

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

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

我们可以采用生成方法,

%% 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。因此,如果需要,请添加剪辑 !

你会做这样的事:

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

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

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 -> THEN ; ELSE)(或剪切)的需要。

  • 它避免了多次比较两个列表以获得最小长度的列表(与参数的顺序无关)。

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

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

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

这里有一个例子:

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