2015-05-29 56 views
4

我在读Learn You Some Erlang,我在Recursion一章中找到了这个例子。是++操作符比|更昂贵Erlang的运算符?

tail_sublist(_, 0, SubList) -> SubList; 
tail_sublist([], _, SubList) -> SubList; 
tail_sublist([H|T], N, SubList) when N > 0 -> 
tail_sublist(T, N-1, [H|SubList]). 

由于笔者接着解释,有我们的代码致命缺陷。因为这样产生的子列表将是相反的,我们将不得不重新反转它们以获得正确的输出。相比之下,我所做的就是使用++运算符来避免稍后反转列表。

sublist_tail([],_,Acc) -> Acc; 
sublist_tail(_,0,Acc) -> Acc; 
sublist_tail([H|T],N,Acc) -> sublist_tail(T,N-1,Acc++[H]). 

我的问题是,++运营商比|运营商更贵?如果是这样,我的解决方案(使用++运算符)仍然比作者的解决方案慢(包括反转列表以获得正确的输出)?

+2

运算符++代价高昂的操作,为了获得需要运行左边参数的结果。 –

回答

2

是++操作符比|运营商?

这取决于。如果你正确使用它,那么不。当你有一个很大的左手边操作数时,++是唯一危险的。

每次“++” - 运算符是在左侧列表中调用(如:List1 ++ List2),您要创建一个新的列表,这是你的左手操作数(List1)的副本。然后每个复制操作都有一个运行时间,这取决于您的迭代的持续增长的长度。

因此,如果您预先将值设置为“头”,则不必在每个步骤中对整个列表执行复制操作。这也意味着,积累与++在列表的头就不会那么糟糕,因为只有“H”值在每次迭代复制一次:

sublist_tail([H|T],N,Acc) -> sublist_tail(T,N-1,[H]++Acc). 

但如果你已经积累头 - 第一(因此以后无论如何反向),你可以用利弊运营商(|

sublist_tail([H|T],N,Acc) -> sublist_tail(T,N-1,[H|Acc]). 

这是“正确”的方式做到这一点,因为(请纠正我,如果我错了++只是语法上的糖,在内部使用cons-operator(|)。

+0

感谢您的解释! – tMJ

7

你可能想了解这个问题在Erlang efficiency guide,因为它说,通过|建筑列表,然后倒车,结果比使用附加++运营商更高效。如果你想知道的性能差异,使用timer:tc

1> timer:tc(fun() -> lists:reverse(lists:foldl(fun(V, Acc) -> [V|Acc] end, [], lists:seq(1,1000))) end). 
{1627, 
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, 
    23,24,25,26,27|...]} 
2> timer:tc(fun() -> lists:foldl(fun(V, Acc) -> Acc++[V] end, [], lists:seq(1,1000)) end). 
{6216, 
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, 
    23,24,25,26,27|...]} 

这两种方法创建的1000个整数名单,但基于二郎/ OTP 17.5这些测量显示预谋/倒车版本大约是4倍比追加版本快(YMMV当然)。

+0

这将是值得注意的将会发生什么10000个数字:-) –

+1

^和100000个数字。 :D – tMJ

+0

非常感谢史蒂夫! – tMJ

相关问题