Scala Isn';那不是尾部递归风格的代码吗?

Scala Isn';那不是尾部递归风格的代码吗?,scala,functional-programming,tail-recursion,Scala,Functional Programming,Tail Recursion,我是Scala的新手,在阅读David Pollack的Begging Scala时尝试一下。 他定义了一个简单的递归函数,用于加载文件中的所有字符串: def allStrings(expr: => String): List[String] = expr match { case null => Nil case w => w :: allStrings(expr) } 它优雅而令人敬畏,只是当我试图加载一个巨大的字典文件时,它抛出了一个StackOver

我是Scala的新手,在阅读David Pollack的Begging Scala时尝试一下。 他定义了一个简单的递归函数,用于加载文件中的所有字符串:

def allStrings(expr: => String): List[String] = expr match {
    case null => Nil
    case w => w :: allStrings(expr)
}
它优雅而令人敬畏,只是当我试图加载一个巨大的字典文件时,它抛出了一个StackOverflow异常

现在据我所知,Scala支持尾部递归,所以函数调用不可能溢出堆栈,可能编译器不识别它?所以在谷歌搜索之后,我尝试了@tailrec注释来帮助编译器,但是它说

error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
def allStrings(expr: => String): List[String] =

我理解尾部递归错了吗?如何修复此代码?

如果最后一次调用是对方法本身的调用,Scala只能对此进行优化

最后一个调用不是调用
allStrings
,而是调用
(cons)方法

使此尾部递归的一种方法是添加累加器参数,例如:

def allStrings(expr: => String, acc: List[String] = Nil): List[String] =
  expr match {
    case null => acc
    case w => allStrings(expr, w :: acc)
  }
为了防止累加器泄漏到API中,可以将尾部递归方法定义为嵌套方法:

def allStrings(expr: => String) = {
  def iter(expr: => String, acc: List[String]): List[String] =
    expr match {
      case null => acc
      case w => iter(expr, w :: acc)
    }
  iter(expr, Nil)
}
它不是尾部递归的(而且永远不会是),因为最终操作不是对
所有字符串的递归调用,而是对
方法的调用

解决此问题的最安全方法是使用使用累加器的嵌套方法:

def allStrings(expr: => String) = {
  @tailrec
  def inner(expr: => String, acc: List[String]): List[String] = expr match {
    case null => acc
    case w => inner(expr, w :: acc)
  }
  inner(expr, Nil)
}

在这种特殊情况下,还可以将累加器提升到
allStrings
上的一个参数,将其默认值设置为
Nil
,并避免使用内部方法。但这并不总是可能的,如果您关心互操作,就不能从Java代码中很好地调用它。

复制我的答案时,您忘记了
@tailrec
注释。这不仅是让编译器确认您期望的一种简单方法,也是对后续维护人员的一个有用提示。Kevin,我没有复制您的答案,实际上在您发布时我正在进行编辑。但是你对注释的看法很好,尽管你说了一句尖刻的话,我还是对你的答案投了更高的票:)看到我们同时回答了,我本来打算删除我的注释,但在注意到你使用了默认参数而不是嵌套方法后,我没有删除。当后续编辑在您的答案中添加此选项时,您可以理解它看起来有多么可疑。