这本书我读关于二郎在它的背面有练习,一个是重新创建列表:附加功能。
我能做到这一点简单地使用++运算符,而不是这真的是慢? 而且我觉得练习的要点是用我写列表操作做到这一点。
到目前为止,我能想到的唯一的办法是做这样的事情:
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,我怎么能修改此所以它适用于任意大小的列表中任何给定的量?
你只需要两个参数的concat函数,因为你会被追加到参数之一,这就是你最终会回来。 喜欢的东西(未经测试):
concat(L,[]) ->
L;
concat(L,[H|T]) ->
concat(L ++ [H],T).
该++是追加操作符,你将不得不这样做是有效的。
(上面的想法是返回左参数,如果我们已经没有更多的左,或移动的要素之一从右边到左边后再次调用)。 有大概在做反向的追加,然后终于扭转了答案更高的效率,但嘿...)
(刚看到你的编辑,当然,我的只适用于两件事情追加,但你可以通过在你的名单列表中的每个元素上面的函数递归...)
++只慢误用时,使用小心地是一样快,任何你可以通过手工工艺。 你必须确保你通过在正确的方向列表工作,否则由此产生的追加为O(N ^ 2)。 当我们做X ++ Y,我们必须使X的副本,然后将其前面加上到不被复制年。
在这个函数中,蓄能器出现在++的左手侧,所以追加效率不高。
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
一个绝妙的方法是使用lists:foldr
,
concat(A,B) ->
lists:foldr(fun(X,XS) -> [X|XS] end, B, A).
concat(XS) ->
lists:foldr(fun concat/2, [], XS).
这也是一个很好的锻炼; Tibial写自己foldr相似功能...
-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.