Recursion erlang如何处理混合尾部递归的case语句
假设我这里有一个代码:Recursion erlang如何处理混合尾部递归的case语句,recursion,erlang,case,tail,Recursion,Erlang,Case,Tail,假设我这里有一个代码: 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) ->
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_循环的三个调用都不需要返回任何内容。但erlang是否会认识到这是一种尾部递归和适当的优化?我担心嵌套结构可能会使它无法识别它。是的,它会。Erlang是优化尾部调用所必需的,这显然是一个尾部调用,因为调用函数后不会发生任何事情
我曾经希望Erlang中有一个
tailcall
关键字,这样编译器就可以警告我无效使用,但后来我习惯了。我对Erlang很陌生,但从我收集的信息来看,规则似乎是为了实现尾部递归,函数必须在任何给定的逻辑分支中执行以下两项操作之一:
- 不进行递归调用
- 返回递归调用的值,然后不执行其他操作
递归调用可以嵌套在任意多个
if
、case
、或receive
调用中,只要调用之后没有实际发生任何事情。我认为这是相关的,因为您想知道如何知道编译器是否优化了递归函数。由于您没有使用lists:reverse/1,以下内容可能不适用,但对于具有完全相同问题但具有不同代码示例的其他人来说,这可能非常相关
来自《Erlang效率指南》中关于Erlang性能的八个神话
在R12B和更高版本中,有
一个优化,将在许多方面
大小写减少了使用的单词数量
在堆栈内体递归调用上,
因此,一个递归列表函数体
以及调用
列表:末尾的反向/1将使用
完全相同的内存量
我认为,在某些情况下,你可能需要衡量一下什么是最好的。是的,这是一个很好的例子。需要注意的主要问题是您是否被包装在异常中。在这种情况下,有时异常需要存在于堆栈上,这将使看起来像尾部递归的东西看起来不像是这样 如果调用处于尾部位置,则尾部调用优化适用。尾部位置是“函数返回之前的最后一件事”。注意,在
fact(0) -> 1;
fact(N) -> N * fact(N-1).
对事实的递归调用不在尾部位置,因为在计算了
fact(N-1)
之后,您需要运行continuationN*
(即乘以N
)。+1。对于任何可证明尾部递归的逻辑,Erlang都会根据语言定义对其进行优化。我很高兴您已经习惯了它,因为tailcall
是一个非常糟糕的想法。