二郎尾递归

时间:2013-12-01 20:58:31

标签: recursion erlang tail-recursion erlang-shell

我刚刚开始学习二郎和尾巴递归慢慢地杀了我;我无法理解它。我试图创建一个程序,使列表中的所有其他数字加倍,我试图用尾递归来做这个。

到目前为止我的代码

    stripAndDoubleOdds([H|T]) -> stripAndDoubleOdds([H|T],1,[H|T]).

    stripAndDoubleOdds(F, _, []) -> F;

    stripAndDoubleOdds(_,Index,[H1|T1]) ->

    F = [] ++ 2*lists:nth(Index, [H1|T1]),  

stripAndDoubleOdds(F, Index +2, T1).

当我给它一个数字列表时,我根本没有输出,但如果我给它一个数字,它可以加倍而没有问题。

索引是为了保持当前元素的位置,并且它增加2,这样我得到每个其他整数并加倍。我当前的解决方案涉及提取头部,将其加倍,将其添加到列表中然后将尾部一遍又一遍地通过该过程,直到我得到一个空列表,此时我应该得到我的列表,F返回。例如如果我输入[1,2,3,4,5]我只想让它给我一个列表 [2,6,,10]。

2 个答案:

答案 0 :(得分:1)

所以第一个身体递归(它不再那么慢):

stripAndDoubleOdds([H, _|T]) -> [2*H | stripAndDoubleOdds(T)];
stripAndDoubleOdds([H]) -> [2*H];
stripAndDoubleOdds([]) -> [].

现在尾递归

stripAndDoubleOdds(L) -> lists:reverse(stripAndDoubleOdds(T, [])).

stripAndDoubleOdds([H, _|T], Acc) -> stripAndDoubleOdds(T, [2*H|Acc]);
stripAndDoubleOdds([H], Acc) -> [2*H|Acc];
stripAndDoubleOdds([], Acc) -> Acc.

你也可以制作列表理解版本,这个版本效率不高也不好

stripAndDoubleOdds(L) ->
    [ 2*X || {X, I} <- lists:zip(L, lists:seq(1,length(L))), I rem 2 =:= 1 ].

答案 1 :(得分:1)

要构建一个在列表上工作的尾递归函数,模式始终是相同的:调用除初始列表外还有累加器参数的函数。根据要执行的函数,累加器可以是整数,空列表或算法所需的任何初始值。

对于您的示例,您想要从初始列表创建列表。累加器将是一个空列表,您将在后续调用期间填充该列表。

对于任何递归算法,您需要定义结束案例,它将停止递归并允许返回结果,在您的情况下,它是在初始列表清空时。

我建议你这个易于阅读的解决方案(IMO)

strd(L) -> strd(L,1,[]). % as you did, I use an integer to detect the odd indices

strd([],_,Acc) -> lists:reverse(Acc); 
%    if the order is important, you should reverse the resulr at the end rather than keeping the right order during
%    the evaluation using the ++ operator. The ++ operator force a copy of the whole list while the construction with
%    [A|B] does not need to copy B.
strd([_|Q],0,Acc) -> strd(Q,1,Acc);
strd([H|Q],1,Acc) -> strd(Q,0,[2*H|Acc]). 
%    I simply toggle the integer betwwen 1 and 0 to select the right operation using pattern matching

Hynek示例可以适用于添加第二个案例的任何长度列表:

stripAndDoubleOdds(L) -> stripAndDoubleOdds(L, []).

stripAndDoubleOdds([H, _|T], Acc) -> stripAndDoubleOdds(T, [2*H|Acc]);
stripAndDoubleOdds([H], Acc) -> lists:reverse([2*H|Acc]);
stripAndDoubleOdds(_, Acc) -> lists:reverse(Acc).