Erlang列表操作

3

我有一个元组列表:

L = [{1, [a, b, c]}, {2, [d, e, f]}, {3, [[h, i, j], [k, l, m]]}]

这是我所拥有的:

lists:map(fun({_, B}-> B end, L).

输出结果为:
[[a, b, c], [d, e, f], [[h, i, j], [k, l, m]]]

what I want is:

[[a, b, c], [d, e, f], [h, i, j], [k, l, m]]

这似乎是一个相当简单的问题,但我无法想出如何解决。 请帮助!

3个回答

5

Let's see...

1> L = [{1, [a, b, c]}, {2, [d, e, f]}, {3, [[h, i, j], [k, l, m]]}].
[{1,[a,b,c]},{2,[d,e,f]},{3,[[h,i,j],[k,l,m]]}]

这段代码很简单易懂,但不是尾递归的:

2> lists:foldr(fun ({_,[X|_]=E},A) when is_list(X) -> lists:append(A,E);
                   ({_,E},A) -> [E|A] end,
                [], L).
[[a,b,c],[d,e,f],[h,i,j],[k,l,m]]

虽然不是尾递归,但这并不好,但...

3> lists:reverse(lists:foldl(fun ({_,[X|_]=E},A) when is_list(X) ->
                                     lists:reverse(E,A);
                                 ({_,E},A) -> [E|A] end,
                             [], L)).
[[a,b,c],[d,e,f],[h,i,j],[k,l,m]]

尾递归版本也可以工作(感谢Zed指出lists:reverse/2)。


你用 lists:append(lists:reverse(E),A) 然后最后再 reverse 整个列表的方式,是为了避免每次都将结果追加到 A 的末尾以提高性能吗? - Quincy
3
@ndim: lists:reverse(E, A) =:= lists:append(lists:reverse(E), A)这段代码表示:将列表E翻转后与列表A合并,得到的结果应该和将列表E和A直接拼接后得到的结果相同。 - Zed
@Quincy: 在A的末尾添加需要每次遍历列表。这不可扩展。因此,您需要反向构建列表,并在完成时进行单个lists:reverse/1调用。这是Erlang的标准操作流程。此外,与lists:foldr/3不同,lists:foldl/3被记录为使用尾递归,因此需要更少的堆栈,并且由于按照规范方向遍历列表而更快。总体而言,lists:foldl/3解决方案应该是最强大的。 - ndim
@Quincy:如果您经常使用包含需要被反转的长列表的元组的长列表来调用它,则“lists:foldr/3”解决方案可能会节省一些CPU周期,但代价是一些堆栈内存。您可以设计一个对大多数算法效果不好的负载。 :) - ndim

2

针对您的具体案例,您可以定义以下函数:

group3([], Acc) ->
     Acc;
group3([A,B,C|Tl], Acc) ->
    group3(Tl, [[A,B,C]] ++ Acc).

group3(L) ->
    lists:reverse(group3(L, [])).

然后像这样调用它:

group3(lists:flatten(lists:map(fun({_, B}) -> B end, L))).

希望这足以为您提供一个普遍的策略。

太糟糕了,它应该接受长度为N的列表。感谢你的努力。 - Quincy

1
-module(z).
-export([do/1]).

do([{_,[X|_] = L}|Tl]) when is_list(X) -> L ++ do(Tl);
do([{_, L}       |Tl])                 -> [L|do(Tl)];
do([])                                 -> [].

测试:

1> z:do(L).
[[a,b,c],[d,e,f],[h,i,j],[k,l,m]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接