2011-03-24 46 views
13

比方说,我有这样的代码在这里:如何二郎手柄case语句与尾递归混合

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

回答

16

是的,会的。 Erlang需要优化尾部调用,这显然是一个尾部调用,因为函数调用后没有任何事情发生。

我曾经希望有在二郎一个tailcall关键字,因此编译器可以警告我的无效使用,但后来我就习惯了。

+1

+1。对于任何**可证**尾递归的逻辑,Erlang会根据语言定义对其进行优化。 – 2011-03-24 23:59:44

+0

我很高兴你习惯了它,因为'tailcall'是一个真正可怕的想法。 – 2011-03-25 08:02:16

-1

我是很新,但二郎从我已经收集,规则似乎是为了将尾递归,函数必须做两件事情在任何给定的逻辑分支:

  • 没有做出递归调用
  • 回报递归调用的值后什么也不做它

递归调用可以被嵌套成许多ifcase,或receive,只要你想呼叫只要n事后并没有发生。

2

这个我认为是相关的,因为你是问你如何知道你的递归函数是由编译器优化。由于您没有使用列表:反向/ 1以下可能不适用,但对于具有完全相同问题但具有不同代码示例的其他人可能非常相关。

从二郎效率指南

在R12B及更高版本在二郎山性能的八个神话,有 将在许多 情况下,减少在堆栈上使用 单词的数量优化在身体递归调用中, 使得一个体递归列表函数 和尾递归函数调用 列表:reverse/1在最后将使用与 完全相同的内存量。

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

我想带走的消息是,你可能在某些情况下,以测量看到的将是最好的。

2

是的,这是尾递归。要注意的主要问题是如果你被包装在例外中。在这种情况下,有时候异常需要在栈上生存,并且这会使得看起来像尾递归的东西变成看似不是那样的东西。

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

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

事实递归调用在尾部的位置,因为计算fact(N-1)后,你需要运行延续N * _(即通过N乘)。