【问题标题】:Why does Scala require a return type for recursive functions?为什么 Scala 需要递归函数的返回类型?
【发布时间】:2011-04-13 22:33:06
【问题描述】:

在下面包含的代码 sn-p 中,我有一个递归函数调用,用于在网络调用失败时促进重试(Amazon SimpleDB 偶尔会返回 503 并要求重试。)

当我尝试编译时,Scala 抱怨 recursive method simpledb_update needs result type。

// sends data to SimpleDB. Retries if necessary
def simpledb_update(name: String, metadata: Map[String,String], attempt: Int) = {
 try {
  db(config("simpledb_db")) += (name, metadata)
 } catch {
  case e =>
   // if it fails, try again up to 5 times
  if(attempt < 6)
  {
   Thread.sleep(500)
   simpledb_update(name, metadata, attempt + 1)
   } else
     AUlog(name + ": SimpleDB Failed")
   }
 }

为什么递归函数需要这样做?我的想法是只返回一个真/假布尔值来满足编译器......以下编译得很好。

// sends data to SimpleDB. Retries if necessary
 def simpledb_update(name: String, metadata: Map[String,String], attempt: Int): Boolean = {
 try {
  db(config("simpledb_db")) += (name, metadata)
  true
 } catch {
  case e =>
   // if it fails, try again up to 5 times
   if(attempt < 6)
   {
    Thread.sleep(500)
    simpledb_update(name, metadata, attempt + 1)
   } else
    AUlog(name + ": SimpleDB Failed")
    false
  }
}

【问题讨论】:

  • Psst,如果您使用的是 2.8,请使用 attempt: Int = 0,这样您的原始调用就不需要指定它是第 0 次尝试!此外,如果你做任何深度递归,你可能会溢出堆栈(当然,6 很好)。要检查 Scala 是否可以避免使用堆栈,请在 def 之前使用 @annotation.tailrec 对其进行注释。如果它不是尾递归的,则必须使用堆栈,Scala 会抛出一个错误——那么至少你知道当你删除注解时你在处理什么。
  • 来自 Ruby,这是我的第一个倾向。唉,我在 2.7... 不受支持!我想是时候升级了。

标签: scala


【解决方案1】:

只需从该行中删除=,它将返回Unit,这意味着您不需要返回任何内容。

def simpledb_update(name: String, metadata: Map[String,String], attempt: Int) {

我相信需要返回类型,以确保所有递归路径都具有正确的类型。在普通函数上,类型将从所有返回点推断出来。

【讨论】:

  • 您根本无法从返回点推断类型,因为至少有一个返回点具有您试图推断的类型。
  • @Jorg - 实际上,推断并不难(但它比人们想要的要难)。如果你得到每个不是递归返回值的路径返回值的类型,那一定是返回值的类型。但是,当推入相互递归函数时,这会变得复杂。
  • 对。这就像尾递归。 许多得到的方程是可解的,而且没有太多困难。问题是并非所有方程都是可解的。语言规范需要声明明确的规则,所有可接受的结构都可以由编译器解决,所有不可接受的结构都有一个简短的错误消息,说明它们为什么不可接受。 “递归方法需要明确的返回类型”是 Scala 的一条明线规则,希望不会太繁琐。
【解决方案2】:

据我了解,递归函数需要返回类型,因为类型推断算法不足以确定所有递归函数的返回类型。

但是,您不需要编写返回类型,只需声明您已经使用的返回类型:Unit。 Unit 是一种特殊类型,只有一个元素 ()。它也是 Scala 中大多数“语句”的类型,并且是为不需要返回任何内容但仅针对其副作用(就像您的那样)执行的方法声明的返回类型。您可以像其他类型一样将您的方法声明为返回单元

def simpledb_update(name: String, metadata: Map[String,String], attempt: Int):Unit = {

更惯用的 Scala 为返回单元的方法提供了一种特殊的语法,只是省略了返回类型和等号

def simpledb_update(name: String, metadata: Map[String,String], attempt: Int){

根据 scala 样式指南,您应该更喜欢使用等号

http://docs.scala-lang.org/style/declarations.html

【讨论】:

  • 为什么类型推断算法不够强大,无法确定所有递归函数的返回类型?
  • 从字面上引用“不耐烦的 Scala”:一些编程语言(例如 ML 和 Haskell)可以使用 Hindley-Milner 算法推断递归函数的类型。但是,这在面向对象的语言中效果不佳。扩展 Hindley-Milner 算法使其能够处理子类型仍然是一个研究问题。
猜你喜欢
  • 2014-04-09
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2018-07-13
  • 2022-08-03
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多