如何在不使用lists模块的情况下编写Erlang的列表连接?

时间:2009-07-15 12:33:39

标签: list function erlang append

我正在读的关于Erlang的书在后面有练习,一个是重新创建列表:追加功能。

我可以简单地使用++运算符来做到这一点,但这不是很慢吗?我认为练习的目的是使用我编写的列表操作来完成它。

到目前为止,我能想到的唯一方法就是做一些事情:

concat([], _, Results)->
  Results;
concat(_, [], Results)->
  Results;
concat([Ah|At],B,Results) ->
  concat(At,B,[Ah|Results]).

但我知道这是不正确的......

关于如何做到这一点的任何建议?

编辑:为了澄清这个问题,这里有一个示例输入和输出:

输入:[[1,2,3],[],[4,5],[6]] 输出:[1,2,3,4,5,6]

工作一段时间后,我也提出了这个代码:

append([A|[B|[T|[]]]]) ->
  append([A++B|T]);
append([H|T]) ->
  H++T.

但是,这仅适用于列表大小为3的情况。如何修改此列表以使其适用于任意给定数量的随机大小的列表?

4 个答案:

答案 0 :(得分:14)

++只有在错误使用时才会很慢,小心使用它就像你手工制作的任何东西一样快。您必须确保以正确的方向处理列表,否则结果附加为O(N ^ 2)。当我们做X ++ Y时,我们必须复制X,然后将它添加到未复制的Y.

在此函数中,累加器出现在++的左侧,因此追加效率不高。

concatl(Lst) ->
    concatl(Lst, []).
concatl([], Acc) ->
    Acc;
concatl([H|T], Acc) ->
    concatl(T, Acc ++ H).

这个函数表现得更好,即使它不是尾递归的。

concat([]) -> [];
concat([H|T]) ->
    H ++ concat(T).

在这种情况下,重写为尾递归只是一个适度的改进:

concat2(Lst) ->
    concat2(lists:reverse(Lst), []).
concat2([], Acc) -> Acc;
concat2([H|T], Acc) ->
    concat2(T, H ++ Acc).

大输入列表上的时间显示了改进的巨大程度。 (时间以微秒为单位。)

41> Time(fun() -> test:concatl([lists:seq(1,1000) || X <- lists:seq(1,1000)]) end).
14539061
40> Time(fun() -> test:concat([lists:seq(1,1000) || X <- lists:seq(1,1000)]) end).
245356
42> Time(fun() -> test:concat2([lists:seq(1,1000) || X <- lists:seq(1,1000)]) end).
211571

答案 1 :(得分:4)

你的concat函数只需要两个参数,因为你将附加一个参数,这就是你最终会返回的参数。像(未经测试)的东西:

concat(L,[]) ->
   L;
concat(L,[H|T]) ->
   concat(L ++ [H],T).

++是附加运算符,你必须这样做才能有效。

(上面的想法是,如果我们不再剩下,则返回左参数,或者在从右到左移动其中一个元素后再次调用)。反向执行追加可能会有更高的效率,然后最终扭转答案但是嘿......)

(刚刚看到你的编辑,我的当然只适用于两个要追加的东西,但你可以通过上面的函数递归列表列表中的每个元素......)

答案 2 :(得分:4)

一种巧妙的方法是使用lists:foldr

concat(A,B) ->
    lists:foldr(fun(X,XS) -> [X|XS] end, B, A).

concat(XS) ->
    lists:foldr(fun concat/2, [], XS). 

编写自己的折叠函数也是一个很好的练习...

答案 3 :(得分:0)

    -module(functional).
    -export([my_append/2,helper/2]).

    my_append(L1,L2) ->
      % concatenates lists L1 and L2
        functional:helper(lists:reverse(L1),L2).
    helper(L1,L2) ->
        if
            L1 == [] -> L2;
            L2 == [] -> L1;
            true     -> [H1|T1] = L1,
                        functional:helper(T1,[H1|L2])
        end.