【问题标题】:Why won't Scala optimize tail call with try/catch?为什么 Scala 不使用 try/catch 优化尾调用?
【发布时间】:2012-01-04 04:59:22
【问题描述】:

recent StackOverflow answer 中,我给出了以下递归代码:

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

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

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

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

【问题讨论】:

  • 如果 (n > 1) - 真的吗?不是 n > 0 或 n >= 1?如果 n == 1 (== 0) 会发生什么?
  • @user unknown - 显然你传入的是总尝试次数,而不是重试次数。

标签: scala try-catch tail-recursion


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    要进行尾递归优化,必须将其转换为以下内容:

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

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

    编辑:根据在 cmets 中与 Rex Kerr 的讨论,这是 Scala 中的行为保持转换(但仅当没有 finally 时)。显然,只是 Scala 编译器还没有识别出没有 finallycatch 块的最后一次调用处于尾部调用位置。

    【讨论】:

    • 投机太多,错的太多。字节码不是问题。字节码甚至不理解 catch 块,它只是 try 块中异常的目标。异常处理规范不影响 this 转换;只需尝试将其重写为 while 循环!
    • @Rex 我很高兴得到更了解 Java 字节码的人的纠正,但我确实将其表述为猜测。不过,我不确定我是否理解您的最后一句话。尾递归转换是关于自动将代码重写为类似于 while 循环的东西,而在 catch 块中发生的尾递归显然会阻止编译器这样做。要么这只是一个尚未实现的功能,要么异常处理规范确实会影响这种转换。还是我误会了你?
    • 这是一个尚未实现的功能。如果在 this 情况下将跳转移到 catch 块之外(只要逻辑正确),行为不会有所不同。等效的迭代代码是var i = n; while(true) { try { return(fn) } catch { case e if i>1 => i -= 1; } }
    【解决方案3】:

    我在 SO 的其他地方找到了这个解决方案。基本上,将returnfn 一起使用,这样如果fn 返回无异常,您的函数也会如此。如果fn确实抛出,并且异常满足条件n > 1,则异常被忽略,递归调用发生在catch块之后。

    @tailrec
    def retry[T](n: Int)(fn: => T): T = {
      try {
        return fn
      } catch {
        case e if n > 1 => // fall through to retry below
      }
      retry(n - 1)(fn)
    }
    

    【讨论】:

      猜你喜欢
      • 2013-05-08
      • 1970-01-01
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 2017-04-11
      • 1970-01-01
      • 2012-10-08
      • 2023-01-17
      相关资源
      最近更新 更多