erlang如何处理混合尾递归的case语句

14

假设我有以下代码:

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会认识到这是尾递归并进行适当的优化吗?我担心嵌套结构可能会使它无法识别。

4个回答

17

是的,会。Erlang需要优化尾调用,而且这显然是一个尾调用,因为在函数调用之后什么也不会发生。

我曾经希望Erlang中有一个`tailcall`关键字,以便编译器可以警告我无效的使用方式,但后来我习惯了。


1
对于任何可以被证明为尾递归的逻辑,Erlang 会根据语言定义进行优化。 - Travis Webb
我很高兴你已经习惯了这个,因为tailcall是一个真正糟糕的想法。 - Mazen Harake

2

是的,它是尾递归的。需要注意的主要问题是如果你被包含在异常中。在这种情况下,有时异常需要保留在栈上,这将使看起来是尾递归的东西变成具有欺骗性的非尾递归。

如果调用处于尾位置,则可应用尾调用优化。尾位置是“函数将返回之前的最后一件事”。请注意,在

fact(0) -> 1;
fact(N) -> N * fact(N-1).

对于 fact 的递归调用,并不是尾部调用,因为在计算完 fact(N-1) 后,需要执行继续运行 N * _(即乘以 N)。


1

我认为这很相关,因为您正在询问如何知道递归函数是否被编译器优化。由于您没有使用lists:reverse/1,下面的内容可能不适用,但对于其他人具有相同问题但使用不同代码示例的情况,它可能非常相关。

来自Erlang Efficiency Guide中的The Eight Myths of Erlang Performance

在R12B及更高版本中,有一种优化方法可以在许多情况下减少堆栈上使用的字数,因此,在结尾处调用lists:reverse/1的体递归列表函数和尾递归函数将使用完全相同的内存量。

http://www.erlang.org/doc/efficiency_guide/myths.html#id58884

我认为这个重要的信息是,在某些情况下,您可能需要进行测量以确定什么是最好的。

-1

我对Erlang相当新,但从我所了解的来看,在任何给定的逻辑分支中,要想成为尾递归函数,规则似乎是必须执行以下两个操作之一:

  • 不进行递归调用
  • 返回递归调用的值,并在此后不执行任何其他操作

该递归调用可以嵌套到多个 ifcasereceive 调用,只要在其后面什么都没有发生。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接