【问题标题】:Compiler fooled by mention of recursive function in non-tail position编译器因在非尾部位置提及递归函数而被愚弄
【发布时间】:2012-09-03 14:30:29
【问题描述】:

我正在尝试通过构建(自定义)延续来对结构尾递归进行操作,但编译器不会接受我的代码是尾递归的。一旦我尝试声明一个在非尾位置引用递归函数的函数文字,即使我没有在这里调用该函数,它也会引发错误。以下是触发错误的一个经过大量提炼的虚拟示例:

import scala.annotation.tailrec
object Test extends App {
  @tailrec
  def tailrectest(i: Int): Int = i match {
    case i if i > 0 => {
      val x = () => tailrectest(10)
      tailrectest(i - 1)
    }
    case 0 => 0
  }
}

我明白了

could not optimize @tailrec annotated method tailrectest: it contains a recursive call not in tail position

指的是val x = () => tailrectest(10)这一行

【问题讨论】:

  • 那么您是在使函数尾部递归时遇到问题,还是您在抱怨编译器没有进行详细的流分析来确定非尾部位置的调用实际上 i> 发生?

标签: scala functional-programming tail-recursion


【解决方案1】:

我认为问题是由于当您将(递归)调用嵌入到函数变量x 中时,编译器通常无法推断它是否会被调用(在这种简单的情况下,它将是虽然可能)。所以为了安全起见,它会抱怨它在函数体中发生的任何递归调用。

一旦你对一个变量进行递归调用,该变量就可以从函数中逃逸(例如被函数返回,或者存储在某个全局状态中等),因此它不能再被优化为尾部-递归循环。

也许发布您希望如何使用x,以便我们可以尝试找到具体的解决方案。

【讨论】:

    【解决方案2】:

    我完全同意 Petr Pudlák 的回答。 但是对于它的价值,有一个出路:定义一个帮助方法来返回一个包装函数到tailrectest:

    import scala.annotation.tailrec
    object Test extends App {
      def tailrectest_ = tailrectest _
      @tailrec
      def tailrectest(i: Int): Int = i match {
        case i if i > 0 => {
          val x = () => tailrectest_(10)
          tailrectest(i - 1)
        }
        case 0 => 0
      }
    }
    

    这给代码增加了一些噪音,但至少它有效。

    但是,如果您要做的是构建某种延续,那么您的真实世界代码肯定必须在闭包中捕获一些本地上下文,这排除了我的上述解决方案。在这种情况下,我看不到一个简单的出路。

    更新(2013 年 3 月 11 日):

    Petr Pudlak found a similar but superior solution in another question: http://stackoverflow.com/questions/15334611/how-to-make-a-tail-recusive-method-that-can-also-refer-to-itself-in-a-non-tail-r
    
    By using an inner function, we can actually capture local state, which make it fully usable. Here is his solution, applied to entropyslave's original question:
    
    import scala.annotation.tailrec
    
    object Test extends App {
      def tailrectest(i: Int): Int = {
        @tailrec
        def tailrectestImpl(i: Int): Int = {
          i match {
            case i if i > 0 =>
              val x = () => tailrectest(10)
              tailrectestImpl(i - 1)
            case 0 => 0
          }
        }
        tailrectest( i )
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-11
      • 2010-10-20
      • 1970-01-01
      • 2016-04-07
      • 2015-05-09
      • 2018-03-17
      • 1970-01-01
      • 2012-07-29
      相关资源
      最近更新 更多