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

时间:2011-03-24 21:42:09

标签: 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);
        _ ->
            im_dead_now
        end
    {die} -> im_dead_now;
    _ -> do_recv_loop(State)
    end.

现在,理论上这是尾递归的,因为对do_recv_loop的三次调用都不需要返回任何内容。但是,erlang会认识到这是尾递归并适当优化吗?我担心嵌套结构可能使它无法识别它。

4 个答案:

答案 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很新,但是根据我收集的内容,规则似乎是为了进行尾递归,函数必须在任何给定的逻辑分支中执行以下两项操作之一:

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

该递归调用可以根据需要嵌套到尽可能多的ifcasereceive调用中,只要它后面没有实际发生。