阅读更多
ErLang Map 函数的尾递归形式
循环语句需要计数器变量控制循环次数,而计数器变量需要多次改变数值。ErLang语言不支持可以重复赋值的变量,因而也不支持循环语句。
ErLang程序员只能用递归表示循环。有些服务程序需要运行在在一个无穷循环中。
while true
listen and respond to user request ….
这种情况下,ErLang程序也要写成无穷递归的形式。在ErLang中,无穷递归必须写成尾递归(Tail Recursion)的形式。因为ErLang是栈语言,如果是非尾递归的递归形式,无穷递归必然引起运行栈的无限膨胀。
下面我们来看一个非尾递归形式转化为尾递归的例子。
ErLang不支持循环语句,而是采用Map、Reduce等便利函数把递归形式简化为类似循环的形式。
map函数的定义为,用一个函数处理一个List中的每一个元素,并且产生每一个结果元素,这些结果元素构成一个新的List,作为返回值。
map函数的调用形式为:
map(
%% 定义一个匿名函数,作为函数体。函数参数就是List中的每一个元素
fun(Item) ->
do something with Item
return a Result Item of processing the Item
end,
List %% 第二个参数List。表示要处理的源List。
)
map函数的具体例子就不多说了。这是比较常见的函数调用。
我们来看map函数的实现。
map(Visit, [] ) -> [] ;
map(Visit, [H | T]) -> [Vsit(H) | map(Visit, T)].
我们看到,上述实现方法里面的递归调用,不是尾递归。
因此运行的过程中,运行栈不断地增长,长到头之后,运行栈再递减。
下面我们把map函数改写成尾递归形式。
由于ErLang的变量都是final的,不能重复赋值。因此我们只能够给List添加一个头,而不能添加尾。所以,我们要在最后把处理结果倒置一下。
map(Visit, L) ->
R = process( Visit, L, [] ),
lists:reverse( R ).
process( Visit, [], R ) -> R;
process( Visit, [H | T], R) ->
RH = Visit( H ),
process(Visit, T, [RH | R]).
我们看到,实际上,我们相当于用一个存放结果的R List代替了运行栈。每一次处理元素的结果都会放入到R这个List当中。
上述代码是尾递归形式,在运行过程,运行栈保持不变。当然,R这个结果List却在不断地增长。相当于自己用一个List代替了运行栈。只不过自己维护的这个List是在堆里面分配的,不受到运行栈大小的限制,只受到内存的限制。