在列表上进行递归时奇数返回

时间:2014-10-13 21:12:16

标签: list erlang

Erlang noob在这里,我有一个函数,它接受任何数字并将其分解为单独的列表中的数字。例如,654 - > [6,5,4]

我的代码如下

digitize(X) when X > 0 ->
    [digitize(X div 10)| (X rem 10)];

digitize(0) ->
    [].

但是回报很奇怪。对于输入654,返回将是:[[[[] | 6] | 5] | 4]。我的代码出了什么问题?

1 个答案:

答案 0 :(得分:8)

简而言之,你回来的名单太多了:)

有时在设计递归时,最终会更容易对它们进行处理。这可能更有意义,因为它是堆栈展开的方式。所以让我们试着打破你的榜样。

  • 最后,您digitize调用0。在这种情况下,它返回空列表[]
  • 然后,当这个空列表成为列表的头部时,(digitize替换之后,我们跳过更高级别,其中6使用[digitize(6 div 10)| (6 rem 10)];(带有[ [] | 6];}进行调用,这将返回到下一个级别。
  • 我们通过65来电话放松,其中[ [] | 6];创建的返回值为head,(65 rem 10)为尾。所以我们再次列出,第一个元素(头部)是列表,第二个(尾部)是一个数字(来自rem
  • 等等。

希望你看到问题所在。现在让我们尝试找到一个解决方案,让我们描述你的算法。在每个递归级别上,您基本上将(X rem 10)(“X中的最小数字”)添加到更大的数字列表(递归)。或者以编程术语发言,您可以附加到列表中。问题是头尾是更像是从头开始/弹出,其中Head是一个元素而Tail是一个列表。幸运的是有lists:append/2 function,甚至语法糖允许添加两个列表:

digitize(X) when X > 0 ->
    digitize(X div 10) ++ [X rem 10];

digitize(0) ->
    [].

这很好用。

这些并不是真正的问题,但是我们可以改进缺少尾递归和++运算符,它的效率远低于[H | T]。所以我们可以在我们的函数上投入更少的精力,并像这样重写它:

digitize(Int) ->
    lists:reverse(digitize(Int, _Acc = []).

digitize(Int, Acc) when X > 0 ->
    digitize(X div 10, [(X rem 10) | Acc]);
digitize(0, Acc) ->
    Acc.

我鼓励您更多地调查此代码,即使以前的版本已经足够了。试着理解为什么这会更快,为什么我们可以使用头尾,为什么我们必须在最后反转列表。 http://learnyousomeerlang.com/recursion是一个很好的开始。祝你好运!