erlang循环结构:尾递归,列表解析

最近看到一道erlang面试题,要求分别用尾递归,lists模块,列表解析找出0-9的偶数。

-module(test).  
-export([tail_loop/0, lists_func/0, list_comp/0]).

% 尾递归
tail_loop() ->
	tail_loop( get_num(), []).

tail_loop([], List) ->
	List;
tail_loop([F | Other], List) ->
	tail_loop( Other, List ++ (if F rem 2 == 0 -> [F]; true -> [] end) ).

% lists模块	
lists_func() ->
	lists:foldl(fun(X, List) ->
	 if X rem 2 == 0 -> List ++ [X];
	  true -> List
	 end
	end, [], get_num()).
 
% 列表解析
list_comp() ->
	[X ||  X<- get_num(), X rem 2 == 0].
	
% 生成0到9的数字
get_num() ->
	lists:seq(0,9).

我们知道,ErLang不支持变量重复赋值,因而也不支持循环语句。erlang能使用的循环结构只有递归和列表解析。

现在看下erlang递归,erlang这里主要用的是尾递归。

先看下erlang递归和尾递归的区别,如下例子:

% 递归
loop(0) -> 
    1; 
loop(N) -> 
   N * loop(N-1).

% 尾递归
tail_loop(N)-> 
    tail_loop(N, 1).

tail_loop(0, R)-> 
    R; 
tail_loop(N, R) -> 
    tail_loop(N-1, N *R). 

不难看出,erlang尾递归是通过参数来传递实际结果。普通递归用到的栈空间和列表的长度成正比,尾递归不需要再次申请栈空间。如果递归的次数过多,显然尾递归比较合适。至于说哪个递归速度快,erlang说法有争议

It depends. On Solaris/Sparc, the body-recursive function seems to be slightly faster, even for lists with very many elements. On the x86 architecture, tail-recursion was up to about 30 percent faster.

接下来看看erlang列表解析,看个例子:

1> [X || X <- [1,2,a,3,4,b,5,6]].
[1,2,a,3,4,b,5,6]

2> [X || X <- [1,2,a,3,4,b,5,6], X > 3].
[a,4,b,5,6]

3> [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].
[4,5,6]

4> [{X, Y} || X <- [1,2,3], Y <- [a,b]].
[{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}]

在erlang列表解析表达式中,|| 左边用以生成列表元素,相当于构造器;右边由赋值语句和条件语句构成,也可以只有赋值语句。
实际上,erlang列表解析在运行时会转换成一个临时函数

% 列表解析
[Expr(E) || E <- List]

% 解析成的临时函数
'lc^0'([E|Tail], Expr) ->
    [Expr(E)|'lc^0'(Tail, Expr)];
'lc^0'([], _Expr) -> [].
总的来说,列表递归函数和尾递归加反转没有太大差别。因此,可以忽略列表函数的性能损失(R12B)。


参考

http://blog.csdn.net/mycwq/article/details/21631123

http://www.erlang.org/doc/efficiency_guide/listHandling.html

http://www.erlang.org/doc/programming_examples/list_comprehensions.html

http://www.erlang.org/doc/efficiency_guide/myths.html#tail_recursive


你可能感兴趣的:(erlang循环结构:尾递归,列表解析)