我与斯卡拉之间存在一些误解
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
}
---编辑:
如果我在return
使用"return foo(list.tail, count + 1)'
,它会有效。
但它并没有解释(对我来说)为什么“返回1”在上面不起作用。
答案 0 :(得分:8)
如果您在下面阅读我的完整说明,那么您的三个问题的答案应该都清楚,但这里有一个简短明了的摘要,方便每个人:
count
,其默认值为0
- 因此返回0
并打印0
。如果您使用count=5
调用它,则会打印5
。 (请参阅下面使用println
的示例。)list
为空的情况。如果list
非空,则返回count
。 (再次,请参阅下面使用println
的示例。)以下是Odersky的 Scala编程的引用(第一版可在线获取):
方法的推荐样式实际上是为了避免使用显式的,尤其是多个return语句。相反,将每个方法视为一个表达式,该表达式返回一个值。这种理念将鼓励您将方法设计得非常小,将较大的方法分解为多个较小的方法。另一方面,设计选择取决于设计上下文,而Scala可以轻松编写具有多个显式返回的方法(如果这是您想要的)。 [link]
在Scala中,您很少使用return
关键字,而是利用表达式中的所有内容将返回值传播回方法的顶级表达式,然后将该结果用作返回值。您可以将return
视为更像break
或goto
的内容,这会破坏正常的控制流,并可能使您的代码更难以推理。
Scala没有像Java这样的语句,而是所有内容都是表达式,这意味着所有内容都返回一个值。这就是为什么Scala有Unit
而不是void
的原因之一 - 因为即使是Java中void
的东西也需要在Scala中返回一个值。以下是一些关于表达式如何与您的代码相关的示例:
1+1
的结果为2
,x.y()
的结果是方法调用的返回值。if
/ else
结构的行为更像Java ternary operator。因此,if (x) y else z
相当于Java中的x ? y : z
。您使用的单独if
与if (x) y else Unit
相同。o.c()
。您可以使用the comma operator in C/C++:(o.a(), o.b(), o.c())
制作类似的结构。 Java实际上没有这样的东西。return
关键字打破表达式中的正常控制流,导致当前方法立即返回给定值。您可以认为它类似于抛出异常,因为它是正常控制流的一个例外,并且因为(如throw
关键字)生成的表达式具有类型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
您应该重构方法,以便body是表达式的值,返回值只是该表达式的结果。看起来您正在尝试编写一个返回列表中项目数的方法。如果是这种情况,这就是我改变它的方式:
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
。
答案 1 :(得分:1)
你从第一次调用(即count
)返回0
的值,而不是来自foo的递归调用的值。
更准确地说,在您的代码中,您不使用foo的递归调用的返回值。
以下是解决问题的方法:
def foo(list:List[Int], count:Int = 0): Int = {
if (list.isEmpty) {
1
} else {
foo(list.tail, count + 1)
}
}
这样,您获得了1
。
顺便说一下,不要使用return
。它并不总是你所期望的。
在Scala中,函数隐式返回最后一个值。您无需明确写入return
。
答案 2 :(得分:1)
你在foo(list.tail,count + 1)前面没有返回的事实意味着,在你从递归返回后,执行正在下降并返回count。由于0作为count的默认值传递,因此一旦从所有递归调用返回,函数将返回count的原始值。
如果您将以下println添加到代码中,则可以看到这种情况:
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" ...
println ("returned from foo " + count)
count
}
要解决此问题,您应该在foo(list.tail .....)前添加一个返回。
答案 3 :(得分:1)
你的return
有效,而不是你期望的方式,因为你忽略了它的价值。如果你要传递一个空列表,你会得到1你所期望的。
因为您没有传递空列表,所以原始代码的工作方式如下:
foo
使用3个元素的List调用并计数0(调用此递归1)return
foo
,现在有2个元素,计数1(递归级别2)return
foo
,现在有1个元素,数量为2(递归级别3)return
foo
没有元素并且计数3(递归级别4)return
输入块并返回1 foo
的结果我们刚回来既没有分配也没有返回,所以它被忽略了。我们继续下一行并返回计数,这与传入的值相同,2 foo
的返回值,而是返回原始count
count
的值为0,这是最终结果答案 4 :(得分:0)
你在程序中返回一个常量并且用0初始化的计数,这就是你在递归的最高层返回的内容。