++运算符比|更昂贵Erlang中的运算符?

时间:2015-05-29 12:24:17

标签: erlang

我正在阅读Learn You Some Erlang,我在Recursion章节中找到了这个例子。

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]).

我的问题是, ++运算符比|运算符贵吗?如果是,将是我的解决方案(使用{{1}与作者的解决方案(包括反转列表以获得正确的输出)相比,仍然很慢?

2 个答案:

答案 0 :(得分:6)

您可能希望在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个整数的列表,但这些基于Erlang / OTP 17.5的测量显示,前置/反转版本比附加版本(当然是YMMV)大约快4倍。

答案 1 :(得分:2)

  

是++运算符比|更昂贵操作

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

每次在左侧List(例如:++)上调用“List1 ++ List2” - 运算符时,您将创建一个新的List,它是左侧操作数的副本(List1)。然后,每个复制操作都有一个运行时,它取决于List1的长度(随迭代次数不断增长)。

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

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

但是如果你已经积累了头部优先(因此无论如何必须稍后反转),你可以使用cons-operator(|

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

这是'正确'的方式,因为(如果我错了请纠正我++只是语法糖并且在内部用cons-operator实现({{1 }})。