如果您阅读了下面我的完整解释,那么您的三个问题的答案应该都很清楚,但为了方便大家,这里有一个简短而明确的摘要:
- 为什么会打印 0?这是因为方法调用正在返回
count
,它有一个默认值——0
所以它返回0
并且你打印0
。如果你用count=5
then 调用它,它会打印出来5
。(请参见println
下面使用的示例。)
- 为什么即使没有“返回”递归也能工作(当它在函数中间时,但不是在最后)?您正在进行递归调用,因此递归发生了,但您没有返回递归调用的结果。
- 为什么不返回 1?当我明确使用“return”时?确实如此,但仅在
list
为空的情况下。如果list
是非空的,那么它会返回count
。(再次,请参见println
下面使用的示例。)
这是 Odersky 引用的Programming in Scala的一段话(第一版可在线获取):
推荐的方法风格实际上是避免使用显式的,尤其是多个返回语句。相反,可以将每个方法视为产生一个值并返回的表达式。这种理念将鼓励您将方法制作得非常小,将较大的方法分解为多个较小的方法。另一方面,设计选择取决于设计上下文,如果您愿意,Scala 可以轻松编写具有多个显式返回的方法。[关联]
在 Scala 中,您很少使用return
关键字,而是利用表达式中的所有内容将返回值传播回方法的顶级表达式,然后将该结果用作返回值。您可以将其return
视为更像break
or的东西goto
,它会破坏正常的控制流程并可能使您的代码更难推理。
Scala 没有像 Java 这样的语句,而是一切都是表达式,这意味着一切都返回一个值。这就是为什么 Scala 有Unit
而不是的原因之一——void
因为即使是void
在 Java 中的东西也需要在 Scala 中返回一个值。以下是一些与您的代码相关的表达式如何工作的示例:
- Java 中的表达式在 Scala 中的行为相同。这意味着
1+1
is的结果2
,而 的结果x.y()
是方法调用的返回值。
- Java 有 if语句,但 Scala 有 if表达式。这意味着 Scala
if
/else
构造的行为更像Java 三元运算符。因此,if (x) y else z
相当于x ? y : z
在Java中。一个像你用的孤单if
是一样的if (x) y else Unit
。
- Java 中的代码块是由一组语句组成的语句,但在 Scala 中,它是由一组表达式组成的表达式。代码块的结果是块中最后一个表达式的结果。因此,{ oa(); 的结果 ob(); oc() } 是
o.c()
返回的任何内容。您可以使用C/C++ 中的逗号运算符进行类似的构造:(o.a(), o.b(), o.c())
。Java真的没有这样的东西。
- 关键字打破了表达式中的
return
正常控制流,导致当前方法立即返回给定值。你可以把它想象成抛出一个异常,既因为它是正常控制流的异常,又因为(像throw
关键字一样)结果表达式具有 type Nothing
。该Nothing
类型用于指示从不返回值的表达式,因此在类型推断期间基本上可以忽略。这是一个简单的示例,显示return
结果类型为Nothing
:
def f(x: Int): Int = {
val nothing: Nothing = { return x }
throw new RuntimeException("Can't reach here.")
}
基于这一切,我们可以看看你的方法,看看发生了什么:
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
}
现在您应该能够看到它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
您应该重构您的方法,使主体的值是一个表达式,而返回值只是该表达式的结果。看起来您正在尝试编写一个返回列表中项目数的方法。如果是这样的话,我会这样改变它:
def foo(list:List[Int], count:Int = 0): Int = {
if (list.isEmpty) count
else foo(list.tail, count + 1)
}
以这种方式编写时,在基本情况下(列表为空),它返回当前项目计数,否则返回列表尾部递归调用的结果count+1
。
如果您真的希望它始终返回1
,则可以将其更改为if (list.isEmpty) 1
,并且它将始终返回1
,因为基本情况将始终返回1
。