【问题标题】:Erlang tail recursionErlang尾递归
【发布时间】:2013-12-17 12:08:17
【问题描述】:

我刚刚开始学习 erlang,尾递归正在慢慢杀死我;我无法理解它。我正在尝试制作一个将列表中所有其他数字翻倍的程序,并且我正在尝试使用尾递归来做到这一点。

这是我目前的代码

    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).

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

Index 是为了保持当前元素的位置,它会增加 2,这样我就可以每隔一个整数得到一个整数并将它加倍。我当前的解决方案包括提取头部,将其加倍,将其添加到列表中,然后一遍又一遍地将尾部传递给该过程,直到我得到一个空列表,此时我应该得到我的列表 F 回来。例如如果我输入 [1,2,3,4,5] 我只希望它给我一个列表 [2,6,,10]。

【问题讨论】:

    标签: recursion erlang tail-recursion erlang-shell


    【解决方案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 ].
    

    【讨论】:

    • 谢谢你,这确实让我的生活变得更好;我已经用纸找到了它,所以现在更清楚了。
    • 该功能不适用于奇数长度的输入列表,最后一项丢失。
    【解决方案2】:

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

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

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

    我向您推荐这个更易于阅读的解决方案 (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).
    

    【讨论】:

    • 我喜欢你对递归及其相关模式的解释。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2018-03-17
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    相关资源
    最近更新 更多