为什么Scala不会使用try/catch优化尾调用?

lee*_*777 17 scala tail-recursion try-catch

最近的StackOverflow答案中,我给出了以下递归代码:

def retry[T](n: Int)(fn: => T): T = {
  try {
    fn
  } catch {
    case e if n > 1 =>
      retry(n - 1)(fn)
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我添加@tailrec注释,我得到:

无法优化@tailrec带注释的方法重试:它包含一个不在尾部位置的递归调用.

我能够破解尾部递归替代方案,但我仍然想知道为什么这没有优化.为什么不?

Ben*_*Ben 10

要进行尾递归优化,必须将其转换为如下所示:

def retry[T](n: Int)(fn: => T): T = {
  START:
    try {
      fn
    } catch {
      case e if n > 1 =>
        n = n - 1
        GOTO START
    }
}
Run Code Online (Sandbox Code Playgroud)

当它执行GOTOto循环时,它必须离开catch块的范围.但在原始的递归版本中,递归调用的执行仍然在catch块内.如果语言允许这可能会改变代码的含义,那么这将不是有效的优化.

编辑:在评论中与Rex Kerr的讨论中,这是Scala中的行为保留转换(但仅在没有时finally).显然,只是Scala编译器还没有认识到catch没有的块的最后一次调用finally是在尾调用位置.


Rex*_*err 7

我不认为以尾递归形式放置代码的已实现转换列表包括遍历异常处理块.这些特别棘手,即使你可以提出它应该合法的例子(如你所知).(有许多情况看起来不合法(例如,如果有finally块),或需要相当复杂的缠绕/展开规则.)