Erlang noob在这里,我有一个函数,它接受任何数字并将其分解为单独的列表中的数字。例如,654 - > [6,5,4]
我的代码如下
digitize(X) when X > 0 ->
[digitize(X div 10)| (X rem 10)];
digitize(0) ->
[].
但是回报很奇怪。对于输入654,返回将是:[[[[] | 6] | 5] | 4]。我的代码出了什么问题?
答案 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是一个很好的开始。祝你好运!