Scala返回不返回

ses*_*ses 1 recursion scala return

我和Scala之间存在一些误解

0还是1?

object Fun extends App {

 def foo(list:List[Int], count:Int = 0): Int = {

    if (list.isEmpty) { // when this is true
      return 1   // and we are about to return 1, the code goes to the next line
    }

    foo(list.tail, count + 1) // I know I do not use "return here" ...

    count
  }

  val result = foo( List(1,2,3) )

  println ( result ) // 0

}
Run Code Online (Sandbox Code Playgroud)
  1. 为什么打印0?
  2. 为什么递归工作即使没有"返回"(当它处于函数的中间,但最终没有)?
  3. 为什么不返回1?当我明确使用"返回"时?

---编辑:

如果我return在这里使用它会工作"return foo(list.tail, count + 1)'.它不解释(对我来说)为什么"返回1"在上面不起作用.

Dao*_*Wen 8

如果您在下面阅读我的完整说明,那么您的三个问题的答案应该都清楚,但这里有一个简短明了的摘要,方便每个人:

  1. 为什么打印0?这是因为方法调用正在返回count,其默认值为0-so它返回0并打印0.如果你用count=5它调用它,那么它会打印出来5.(参见println下面的示例.)
  2. 为什么递归工作即使没有"返回"(当它处于函数的中间,但最终没有)?您正在进行递归调用,因此递归发生,但您没有返回递归调用的结果.
  3. 为什么不返回1?当我明确使用"返回"时?它确实如此,但仅在list空的情况下.如果list非空,则返回count.(再次,请参阅println下面的示例.)

以下是Odersky的Scala编程报告(第一版可在线获取):

事实上,推荐的方法样式是避免使用显式的,特别是多个返回语句.相反,将每个方法视为一个表达式,该表达式返回一个值.这种理念将鼓励您将方法设计得非常小,将较大的方法分解为多个较小的方法.另一方面,设计选择取决于设计上下文,而Scala可以轻松编写具有多个显式返回的方法(如果这是您想要的).[链接]

在Scala中,您很少使用return关键字,而是利用表达式中的所有内容将返回值传播回方法的顶级表达式,然后将该结果用作返回值.你能想到的return是更多的东西一样break或者goto,扰乱正常的控制流,可能使你的代码更难推理.

Scala没有像Java这样的语句,而是一切都是表达式,这意味着一切都返回一个值.这就是为什么Scala Unit而不是 - void因为即使是voidJava中的东西需要在Scala中返回一个值的原因之一.以下是有关表达式如何与您的代码相关的一些示例:

  1. Java中表达式的内容在Scala中的作用相同.这意味着结果1+12,结果x.y()是方法调用的返回值.
  2. Java有if 语句,但Scala有if 表达式.这意味着Scala if/ else构造更像Java三元运算符.因此,if (x) y else z相当于x ? y : zJava.if像你使用的孤独是一样的if (x) y else Unit.
  3. Java中的代码块是由一组语句组成的语句,但在Scala中,它是由一组表达式组成的表达式.代码块的结果是块中最后一个表达式的结果.因此,{oa(); OB(); oc()}是o.c()返回的内容.您可以使用C/C++中的逗号运算符进行类似的构造:(o.a(), o.b(), o.c()).Java实际上没有这样的东西.
  4. return关键字打破在表达正常控制流,从而导致当前方法立即返回给定值.您可以认为它有点像抛出异常,因为它是正常控制流的一个例外,并且因为(如throw关键字)生成的表达式具有类型Nothing.该Nothing类型用于表示永远不返回值的表达式,因此在类型推断期间基本上可以忽略.这是一个简单的例子,显示return结果类型为Nothing:
def f(x: Int): Int = {
  val nothing: Nothing = { return x }
  throw new RuntimeException("Can't reach here.")
}
Run Code Online (Sandbox Code Playgroud)

基于这一切,我们可以查看您的方法,看看发生了什么:

 def foo(list:List[Int], count:Int = 0): Int = { 
   // This block (started by the curly brace on the previous line
   // is the top-level expression of this method, therefore its result
   // will be used as the result/return value of this method.
     if (list.isEmpty) {
       return 1 // explicit return (yuck)
     }
     foo(list.tail, count + 1) // recursive call
     count // last statement in block is the result
  }
Run Code Online (Sandbox Code Playgroud)

现在您应该能够看到count正在使用您的方法的结果,除非您通过使用中断正常的控制流return.您可以看到它return正在工作,因为foo(List(), 5)返回1.相反,foo(List(0), 5)返回5因为它使用块的结果count作为返回值.如果你尝试,你可以清楚地看到这一点:

println(foo(List()))      // prints 1 because list is empty
println(foo(List(), 5))   // prints 1 because list is empty
println(foo(List(0)))     // prints 0 because count is 0 (default)
println(foo(List(0), 5))  // prints 5 because count is 5
Run Code Online (Sandbox Code Playgroud)

您应该重构您的方法,以便body是一个表达式的值,返回值只是该表达式的结果.看起来您正在尝试编写一个返回列表中项目数的方法.如果是这样的话,这就是我改变它的方式:

 def foo(list:List[Int], count:Int = 0): Int = {
   if (list.isEmpty) count
   else foo(list.tail, count + 1)
 }
Run Code Online (Sandbox Code Playgroud)

以这种方式编写时,在基本情况下(列表为空)它返回当前项目计数,否则它返回列表尾部的递归调用结果count+1.

如果你真的希望它总是返回,1你可以改为将其if (list.isEmpty) 1改为,并且它将始终返回,1因为基本情况将始终返回1.