用尾递归优化Erlang的lists:map
Erlang的lists库中的map函数是这样写的:
-spec(map/2 :: (fun((D) -> R), [D]) -> [R]).
map(F, [H|T]) ->
[F(H)|map(F, T)];
map(F, []) when is_function(F, 1) -> [].
[F(H)|map(F, T)];
map(F, []) when is_function(F, 1) -> [].
显然这里有个问题," [F(H)|map(F, T)]"这句没有用为递归实现,在list很大的情况下,可能会耗费很大的栈空间,并且在效率上也有很大的损失.下面是通过尾递归实现的list map/2 函数:
-module(map_tail).
-export([map/2]).
-export([map/2]).
map(_, [], R) -> lists:reverse(R);% 最后一次调用,需要把列表反转一下,否则,取得元素顺序会掉转
map(F, [H|T], R) -> map( F, T, [F(H)|R] ). % 这里是尾递归的关键,本来函数生成新状态是放在栈上保存的( [F(H)|map(F, T)];),在尾递归的写法中,状态被转移到第三个参数中,这样,Erlang就可以不用保存每次递归调用的栈了(因为栈上没有保存任何状态信息.)函数本次调用的返回值就是下次调用的返回值.在长长的递归调用抵达最后一次返回时,可以直接把最后一次调用的返回值返回给第一次调用的调用者.这就是尾递归优化的过程.
map(F, L) -> map(F, L, []).% 适配原来的lists:map/2 的接口