遞歸是 Erlang 的重要組成部分。首先,讓我們看看如何通過實現(xiàn) factorial 程序來實現(xiàn)簡單的遞歸。
-module(helloworld). -export([fac/1,start/0]). fac(N) when N == 0 -> 1; fac(N) when N > 0 -> N*fac(N-1). start() -> X = fac(4), io:fwrite("~w",[X]).
關(guān)于上述程序,需要注意以下幾點:
我們首先定義一個名為 fac(N) 的函數(shù)。
我們可以通過遞歸調(diào)用fac(N)來定義遞歸函數(shù)。
上面程序的輸出是:
24
在本節(jié)中,我們將詳細了解遞歸的不同類型及其在Erlang中的用法。
遞歸的一種更實用的方法可以從一個用于確定列表長度的簡單示例中看出。一個列表可以有多個值,如[1,2,3,4]。讓我們使用遞歸來看看如何得到一個列表的長度。
-module(helloworld). -export([len/1,start/0]). len([]) -> 0; len([_|T]) -> 1 + len(T). start() -> X = [1,2,3,4], Y = len(X), io:fwrite("~w",[Y]).
關(guān)于上述程序,需要注意以下幾點:
如果列表為空,則第一個函數(shù)len([])用于特殊情況下的條件。
[H|T] 模式來匹配一個或多個元素的列表,如長度為1的列表將被定義為 [X|[]],而長度為 2 的列表將被定義為 [X|[Y|[]]] 。
注意,第二元素是列表本身。這意味著我們只需要計數(shù)第一個,函數(shù)可以調(diào)用它本身在第二元素上。在列表給定每個值的長度計數(shù)為 1 。
上面程序的輸出將是
4
要了解尾遞歸的工作原理,讓我們了解上一節(jié)中的以下代碼如何工作。
語法
len([]) -> 0; len([_|T]) -> 1 + len(T).
1 + len (Rest)的答案需要找到 len (Rest)的答案。然后函數(shù) len (Rest)本身需要找到另一個函數(shù)調(diào)用的結(jié)果。添加的部分會堆疊起來,直到找到最后一個,然后才能計算出最終的結(jié)果。
尾部遞歸旨在通過在操作發(fā)生時減少它們來消除這種疊加操作。
為了實現(xiàn)這一點,我們需要在函數(shù)中保留一個額外的臨時變量作為參數(shù)。前面提到的臨時變量有時被稱為 accumulator,用作存儲計算結(jié)果的地方,以限制調(diào)用的增長。
讓我們看一下尾遞歸的一個實例
-module(helloworld). -export([tail_len/1,tail_len/2,start/0]). tail_len(L) -> tail_len(L,0). tail_len([], Acc) -> Acc; tail_len([_|T], Acc) -> tail_len(T,Acc+1). start() -> X = [1,2,3,4], Y = tail_len(X), io:fwrite("~w",[Y]).
上面程序的輸出是
4
讓我們看一個遞歸的實例。這次讓我們編寫一個函數(shù),該函數(shù)將整數(shù)作為第一個參數(shù),然后將任何其他項作為第二個參數(shù)。然后,它將創(chuàng)建一個由整數(shù)指定的術(shù)語復本的列表。
讓我們看一下這樣的一個實例-
-module(helloworld). -export([duplicate/2,start/0]). duplicate(0,_) -> []; duplicate(N,Term) when N > 0 -> io:fwrite("~w,~n",[Term]), [Term|duplicate(N-1,Term)]. start() -> duplicate(5,1).
上面程序的輸出將是-
1, 1, 1, 1, 1,
在Erlang中可以使用遞歸沒有任何限制。現(xiàn)在讓我們快速看一下如何使用遞歸來反轉(zhuǎn)列表的元素。可以使用以下程序來完成此操作。
-module(helloworld). -export([tail_reverse/2,start/0]). tail_reverse(L) -> tail_reverse(L,[]). tail_reverse([],Acc) -> Acc; tail_reverse([H|T],Acc) -> tail_reverse(T, [H|Acc]). start() -> X = [1,2,3,4], Y = tail_reverse(X), io:fwrite("~w",[Y]).
上面程序的輸出將是-
[4,3,2,1]
關(guān)于上述程序,需要注意以下幾點:
我們再次使用臨時變量的概念來將列表中的每個元素存儲在一個名為Acc的變量中。
然后遞歸地調(diào)用tail_reverse,但這一次,我們確保最后一個元素先放到新列表中。
然后遞歸地對列表中的每個元素調(diào)用tail_reverse。