1. 程式人生 > >erlang迴圈結構:尾遞迴,列表解析

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