Erlang 风格的 N 长度函数的子列表

Sublists of N length function in Erlang style

我一直在学习 Erlang 并尝试完成一些练习功能。我特别努力制作一个功能,认为这可能是因为我没有考虑 "Erlang" 足够。

有问题的函数接受一个列表和一个子列表大小,然后生成一个元组列表,其中包含子列表之前的元素数、子列表本身和子列表之后的元素数。例如

sublists(1,[a,b,c])=:=[{0,[a],2}, {1,[b],1}, {2,[c],0}].
sublists(2,[a,b,c])=:=[{0,[a,b],1}, {1,[b,c],0}].

我的工作解决方案是

sublists(SubListSize, [H | T]) ->
    Length = length(1, T),
    sublists(SubListSize, Length, Length-SubListSize, [H|T], []).
sublists(_, _, -1, _, Acc) -> lists:reverse(Acc);
sublists(SubSize, Length, Count, [H|T], Acc) ->
    Sub = {Length-SubSize-Count, grab(SubSize, [H|T],[]),Count},
    sublists(SubSize, Length, Count-1, T, [Sub|Acc]).

length(N, []) -> N;
length(N, [_|T]) -> length(N+1, T).

grab(0, _, Acc) -> lists:reverse(Acc);
grab(N, [H|T], Acc) -> grab(N-1, T, [H|Acc]).

但是感觉不太对,不知道有没有更好的办法?

有一个扩展要求使用列表理解重新实现子列表功能。我失败的尝试是

sublist_lc(SubSize, L) ->
    Length = length(0, L),
    Indexed = lists:zip(L, lists:seq(0, Length-1)),
    [{I, X, Length-1-SubSize} || {X,I} <- Indexed, I =< Length-SubSize].

据我了解,列表理解无法预见未来,因此我无法使用之前的抓取功能。这再次让我觉得必须有更好的方法来解决这个问题。

我在下面展示了一些方法。 All 防止请求的子列表长度大于列表长度的情况。所有使用标准 lists module.

中的函数

第一个使用 lists:split/2 来捕获每个子列表和剩余尾列表的长度,并使用计数器 C 来跟踪子列表之前有多少元素。剩余尾列表的长度,名为 Rest,给出了每个子列表后面的元素数。

sublists(N,L) when N =< length(L)  ->
    sublists(N,L,[],0).
sublists(N,L,Acc,C) when N == length(L) ->
    lists:reverse([{C,L,0}|Acc]);
sublists(N,[_|T]=L,Acc,C) ->
    {SL,Rest} = lists:split(N,L),
    sublists(N,T,[{C,SL,length(Rest)}|Acc],C+1).

下一个使用两个计数器列表,一个指示子列表前面有多少元素,另一个指示子列表后面有多少元素。第一个很容易计算,只需从 0 计数到输入列表的长度减去每个子列表的长度,第二个计数器列表正好与第一个相反。这些计数器列表也用于控制递归;当每个只包含一个元素时我们停止,表明我们已经到达最终的子列表并且可以结束递归。此方法使用 lists:sublist/2 调用来获取除最终子列表之外的所有内容。

sublists(N,L) when N =< length(L) ->
    Up = lists:seq(0,length(L)-N),
    Down = lists:reverse(Up),
    sublists(N,L,[],{Up,Down}).
sublists(_,L,Acc,{[U],[D]}) ->
    lists:reverse([{U,L,D}|Acc]);
sublists(N,[_|T]=L,Acc,{[U|UT],[D|DT]}) ->
    sublists(N,T,[{U,lists:sublist(L,N),D}|Acc],{UT,DT}).

最后,这是一个基于列表理解的解决方案。它与之前的解决方案相似,因为它使用两个计数器列表来控制迭代。它还利用 lists:nthtail/2lists:sublist/2 来获取每个子列表,这当然不是很有效;毫无疑问,它可以改进。

sublists(N,L) when N =< length(L) ->
    Up = lists:seq(0,length(L)-N),
    Down = lists:reverse(Up),
    [{U,lists:sublist(lists:nthtail(U,L),N),D} || {U,D} <- lists:zip(Up,Down)].

哦,请注意:您的代码实现了一个名为 length/2 的函数,这有点令人困惑,因为它与标准 length/1 函数同名。我建议避免将您的函数命名为与此类常用标准函数相同的名称。