在erlang中进行尾递归

我从最基本的角度学习Erlang,并且遇到了尾递归函数的问题。 我想我的函数接收一个列表并返回一个新的列表,其中element = element + 1。例如,如果我发送[1,2,3,4,5]作为参数,它必须返回[2,3,4 ,5,6]。 问题是,当我发送确切的参数时,它返回[[[[[[[] | 2] | 3] | 4] | 5] | 6]。

我的代码是这样的:

-module(test).
-export([test/0]).

test()->
  List = [1,2,3,4,5],
  sum_list_2(List).

sum_list_2(List)->
  sum_list_2(List,[]).

sum_list_2([Head|Tail], Result)->
  sum_list_2(Tail,[Result|Head +1]);
sum_list_2([], Result)->
  Result.

但是,如果我将我的功能更改为:

sum_list_2([Head|Tail], Result)->
  sum_list_2(Tail,[Head +1|Result]);
sum_list_2([], Result)->
  Result.

它输出[6,5,4,3,2]即可。 为什么函数不能以相反的方式工作([Result | Head + 1]输出[2,3,4,5,6])?

PS:我知道这个问题是用列表解析来解决的,但我想用递归来实现。


对于这种操作,你应该使用列表理解:

1> L = [1,2,3,4,5,6].
[1,2,3,4,5,6]
2> [X+1 || X <- L].
[2,3,4,5,6,7]

这是做到这一点最快,最习惯的方式。

对你的拳头版本的评论: [Result|Head +1]建立一个不适当的名单。 该结构总是[Head|Tail] ,尾部是列表。 你可以使用Result ++ [Head+1]但是这会在每次递归调用时执行Result列表的副本。

您还可以查看lists:map/2的代码lists:map/2 ,它不是尾递归,但似乎在这种情况下编译器的实际优化工作得很好:

inc([H|T]) -> [H+1|inc(T)];
inc([]) -> [].

[编辑]

列表的内部和隐藏表示看起来像链式列表。 每个元素都包含一个术语和对尾部的引用。 因此,在头部添加一个元素不需要修改现有列表,但在最后添加一些内容需要修改最后一个元素(对空列表的引用被对新子列表的引用所替代)。 由于变量不可变,所以需要修改最后一个元素的修改副本,然后需要修改列表中的前一个元素等等。 据我所知,编译器的优化不会决定变异变量(从文档中扣除)。


以相反顺序生成结果的函数是将新增加的元素添加到Result列表前面的自然Result 。 这并不罕见,推荐的“修复”是简单地list:reverse/1返回前的输出。

在这种情况下,您可以简单地使用++运算符而不是[H|T] “cons”运算符以反方式加入结果,并以正确的顺序给出所需的输出:

sum_list_2([Head|Tail], Result)->
  sum_list_2(Tail, Result ++ [Head + 1]);

不建议这么做,因为++操作符总是复制左手操作数(越来越大),导致算法在O(n ^ 2)时间内运行,而不是[Head + 1 | Tail] [Head + 1 | Tail]版本的O(n)时间。

链接地址: http://www.djcxy.com/p/80691.html

上一篇: Tail Recursions in erlang

下一篇: Set Intersection with Tail Recursion