假设我在这里有这段代码:
do_recv_loop(State) ->
receive
{do,Stuff} ->
case Stuff of
one_thing ->
do_one_thing(),
do_recv_loop(State);
another_thing ->
do_another_thing(),
do_recv_loop(State);
_ ->
im_dead_now
end
{die} -> im_dead_now;
_ -> do_recv_loop(State)
end.
现在,理论上这是尾递归的,因为对do_recv_loop的三次调用都不需要返回任何内容。但是,erlang会认识到这是尾递归并适当优化吗?我担心嵌套结构可能使它无法识别它。
答案 0 :(得分:16)
是的,它会的。 Erlang需要优化尾调用,这显然是尾调用,因为调用函数后没有任何反应。
我曾经希望在Erlang中有一个tailcall
关键字,所以编译器可以警告我无效使用,但后来我习惯了。
答案 1 :(得分:2)
我认为这是相关的,因为您询问的是如何知道编译器是否优化了递归函数。由于您没有使用列表:reverse / 1,下面的内容可能不适用,但对于其他具有完全相同问题但使用不同代码示例的人来说,它可能非常相关。
来自Erlang效率指南中Erlang表演的八大神话
在R12B及更高版本中,有 许多人都会进行优化 case减少了使用的单词数量 在身体递归调用的堆栈上, 这样一个body-recursive list函数 和调用的尾递归函数 列表:反向/ 1末尾将使用 内存量完全相同。
http://www.erlang.org/doc/efficiency_guide/myths.html#id58884
我认为带走的信息是你可能需要在某些情况下衡量一下最好的情况。
答案 2 :(得分:2)
是的,它是尾递归的。需要注意的主要问题是如果你被包含在异常中。在这种情况下,有时异常需要存在于堆栈中,并且这会使得看起来像尾部递归的东西看起来不那么明显。
如果呼叫处于尾部位置,则尾部呼叫优化适用。尾部位置是“函数返回之前的最后一件事”。请注意
fact(0) -> 1;
fact(N) -> N * fact(N-1).
事实的递归调用在尾部位置是而不是,因为在计算fact(N-1)
之后,您需要运行延续N * _
(即乘以N
})。
答案 3 :(得分:-1)
我对Erlang很新,但是根据我收集的内容,规则似乎是为了进行尾递归,函数必须在任何给定的逻辑分支中执行以下两项操作之一:
该递归调用可以根据需要嵌套到尽可能多的if
,case
或receive
调用中,只要它后面没有实际发生。