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

4 回答 4

17

是的,它会的。需要 Erlang 来优化尾调用,这显然是尾调用,因为调用函数后什么都没有发生。

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

于 2011-03-24T21:54:37.217 回答
2

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

来自 Erlang 效率指南中的 Erlang 性能的八个神话

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

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

我认为要传达的信息是,在某些情况下,您可能必须进行衡量才能看到最好的结果。

于 2011-03-24T23:02:58.253 回答
2

是的,它是尾递归的。要注意的主要问题是您是否被包裹在异常中。在这种情况下,有时异常需要存在于堆栈中,这会使看起来尾递归的东西变成看似不是这样的东西。

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

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

对 fact 的递归调用不在尾部位置,因为在fact(N-1)计算之后,您需要运行延续N * _(即乘以N)。

于 2011-03-25T00:36:27.520 回答
-1

我对 Erlang 很陌生,但从我收集到的信息来看,规则似乎是为了尾递归,该函数必须在任何给定的逻辑分支中执行以下两项操作之一:

  • 不进行递归调用
  • 返回递归调用的值,之后什么也不做

该递归调用可以嵌套到任意数量的ifcasereceive调用中,只要它之后没有实际发生任何事情。

于 2011-03-24T22:36:05.937 回答