10

我正在阅读的关于 Erlang 的书的后面有练习,其中之一是重新创建 lists:append 函数。

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

到目前为止,我能想到的唯一方法是执行以下操作:

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

4 回答 4

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
于 2009-07-15T14:59:20.837 回答
4

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

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

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

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

于 2009-07-15T16:29:17.667 回答
4

您的 concat 函数只需要两个参数,因为您将附加到其中一个参数,这就是您最终将返回的参数。类似(未经测试):

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

++ 是附加运算符,您必须这样做才能提高效率。

(上面的想法是如果我们没有更多的left,则返回left参数,或者在将元素之一从右向左移动后再次调用)。反向执行附加然后最终反转答案可能会更有效率,但是嘿......)

(刚刚看到您的编辑,我的当然只适用于要附加的两件事,但您可以通过上述函数对列表列表中的每个元素进行递归......)

于 2009-07-15T13:20:28.167 回答
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.
于 2016-01-17T16:45:53.220 回答