递归是 Erlang 的重要组成部分。首先,让我们看看如何通过实现 factorial 程序来实现简单的递归。
-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]).
关于上述程序,需要注意以下几点:
我们首先定义一个名为 fac(N) 的函数。
我们可以通过递归调用fac(N)来定义递归函数。
上面程序的输出是:
24
在本节中,我们将详细了解递归的不同类型及其在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]).
关于上述程序,需要注意以下几点:
如果列表为空,则第一个函数len([])用于特殊情况下的条件。
[H|T] 模式来匹配一个或多个元素的列表,如长度为1的列表将被定义为 [X|[]],而长度为 2 的列表将被定义为 [X|[Y|[]]] 。
注意,第二元素是列表本身。这意味着我们只需要计数第一个,函数可以调用它本身在第二元素上。在列表给定每个值的长度计数为 1 。
上面程序的输出将是
4
要了解尾递归的工作原理,让我们了解上一节中的以下代码如何工作。
语法
len([]) -> 0; len([_|T]) -> 1 + len(T).
1 + len (Rest)的答案需要找到 len (Rest)的答案。然后函数 len (Rest)本身需要找到另一个函数调用的结果。添加的部分会堆叠起来,直到找到最后一个,然后才能计算出最终的结果。
尾部递归旨在通过在操作发生时减少它们来消除这种叠加操作。
为了实现这一点,我们需要在函数中保留一个额外的临时变量作为参数。前面提到的临时变量有时被称为 accumulator,用作存储计算结果的地方,以限制调用的增长。
让我们看一下尾递归的一个实例
-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
让我们看一个递归的实例。这次让我们编写一个函数,该函数将整数作为第一个参数,然后将任何其他项作为第二个参数。然后,它将创建一个由整数指定的术语复本的列表。
让我们看一下这样的一个实例-
-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中可以使用递归没有任何限制。现在让我们快速看一下如何使用递归来反转列表的元素。可以使用以下程序来完成此操作。
-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]
关于上述程序,需要注意以下几点:
我们再次使用临时变量的概念来将列表中的每个元素存储在一个名为Acc的变量中。
然后递归地调用tail_reverse,但这一次,我们确保最后一个元素先放到新列表中。
然后递归地对列表中的每个元素调用tail_reverse。