1

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

到目前为止,这是我的代码

    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] 的列表。

4

2 回答 2

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).
于 2013-12-01T22:37:34.340 回答
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 ].
于 2013-12-01T21:12:03.783 回答