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)
之后,您需要运行continuation
N*
(即乘以
N
)。

+1。对于任何可证明尾部递归的逻辑,Erlang都会根据语言定义对其进行优化。我很高兴您已经习惯了它,因为
tailcall
是一个非常糟糕的想法。