我是Scala的新手,知道'return'关键字是多余的。但是,在编写递归函数时,如果缺少'return'关键字,则当满足所需条件时,控件不会从调用堆栈返回。
下面是一个代码来检查平衡括号——
使用'return'(工作正常):
def balance(chars: List[Char]): Boolean = {
def parenBalance(char : List[Char]) :Boolean = {
parenBalanceRecur(char, 0 , 0)
}
def parenBalanceRecur(charSequence: List[Char], itr : Int, imbalance : Int) : Boolean = {
if (imbalance < 0) then return false
if (itr == charSequence.length ) {
if (imbalance == 0) then return true else return false
}
if (charSequence(itr) == '(') {
parenBalanceRecur(charSequence, (itr + 1), (imbalance + 1))
}
else if (charSequence(itr) == ')') {
parenBalanceRecur(charSequence, itr + 1, imbalance -1)
}
else {
parenBalanceRecur (charSequence, itr +1, imbalance)
}
}
parenBalance(chars)
}
没有返回(成功计算#1,但没有返回,在#2失败,IndexOutOfBoundsException):
def balance(chars: List[Char]): Boolean = {
def parenBalance(char : List[Char]) :Boolean = {
parenBalanceRecur(char, 0 , 0)
}
def parenBalanceRecur(charSequence: List[Char], itr : Int, imbalance : Int) : Boolean = {
if (imbalance < 0) then false
if (itr == charSequence.length ) {
if (imbalance == 0) then true else false // #1
}
if (charSequence(itr) == '(') { // # 2
parenBalanceRecur(charSequence, (itr + 1), (imbalance + 1))
}
else if (charSequence(itr) == ')') {
parenBalanceRecur(charSequence, itr + 1, imbalance -1)
}
else {
parenBalanceRecur (charSequence, itr +1, imbalance)
}
}
parenBalance(chars)
}
这是尾递归特有的吗?请帮我理解一下。
正如在一些评论中提到的,您不能只是短路路径并返回没有return
关键字的值。仅在方法的最后一个值时不需要return
。在任何情况下,通常,在一个方法中使用多个返回来避免短路是一个很好的实践。在您的示例中,添加两个else
可以构建到每个可能输出的最后一个值的路径,而无需使用return
关键字:
def balance(chars: List[Char]): Boolean = {
def parenBalance(char : List[Char]) :Boolean = {
parenBalanceRecur(char, 0 , 0)
}
@tailrec
def parenBalanceRecur(charSequence: List[Char], itr : Int, imbalance : Int) : Boolean = {
if (imbalance < 0) then false
else if (itr == charSequence.length ) {
if (imbalance == 0) then true else false // #1
}
else if (charSequence(itr) == '(') { // # 2
parenBalanceRecur(charSequence, (itr + 1), (imbalance + 1))
}
else if (charSequence(itr) == ')') {
parenBalanceRecur(charSequence, itr + 1, imbalance -1)
}
else {
parenBalanceRecur (charSequence, itr +1, imbalance)
}
}
parenBalance(chars)
}
目前实现的parenBalanceRecur()
有3个顶级if
表达式,它们每个求值为布尔值,但scala中的规则是只有函数的最后一个表达式是函数的返回值=>前两个直接忽略。
=比;在第二个实现中:
def parenBalanceRecur(charSequence: List[Char], itr : Int, imbalance : Int) : Boolean = {
if (imbalance < 0) then false
if (itr == charSequence.length ) {
if (imbalance == 0) then true else false // #1
}
if (charSequence(itr) == '(') { // # 2
parenBalanceRecur(charSequence, (itr + 1), (imbalance + 1))
}
else if (charSequence(itr) == ')') {
parenBalanceRecur(charSequence, itr + 1, imbalance -1)
}
else {
parenBalanceRecur (charSequence, itr +1, imbalance)
}
}
parenBalance(chars)
}
第一个表达式if (imbalance < 0) then false
将计算为真或假,但该表达式与代码的其余部分断开连接=>这个函数没有对那个布尔值做任何事情。我们也可以写成val thisAchievesNothing = if (imbalance < 0) then false
。我们的thisAchievesNothing
将保存一些值,这是有效的语法,但不是很有用。
同样,:
if (itr == charSequence.length ) {
if (imbalance == 0) then true else false // #1
}
将计算为另一个布尔值,同样被忽略。
最后,最后一个if... else if... else
也将求值为另一个布尔值,并且由于这个是函数的最后一个表达式,因此只有that和that将是返回值。
尝试将parenBalanceRecur
重写为单个if.. else if... else if ...
表达式而不是3,然后行为将与使用return
的实现相同。
(我们倾向于避免使用return
,因为它是做某些事情的语句,而习惯上是编写是某些值的函数/表达式)
除了公认的答案,还有一些值得考虑的事情:
- 使用@tailrec,这样你的递归方法得到尾部调用优化的编译器。否则,对于非常大的列表,它可能会产生堆栈溢出。(虽然这里可能不是这种情况,但在实现尾部递归方法时总是考虑使用此注释)。 Scala中的
List
是一个单链表,所以它不被索引。在每次递归调用中使用cs(iter)
使您的方法在线性时间内获得每个索引的元素。这意味着复杂度是0 (n^2)作为替代方案,要么使用基于索引的chars: Array[Char]
,并在0(1)中给出每个元素,要么继续使用列表,但切换到模式匹配。- 小问题:
if
表达式中只有一条语句的多余花括号;parenBalance
可以移除,直接调用parenBalanceRecur(char, 0 , 0)
作为balance
的最后一条语句;parenBalanceRecur
使用默认值
我在代码中的建议:
def isBalanced(chars: List[Char]): Boolean = {
@tailrec
def balance(cs: List[Char], imbalances: Int = 0): Boolean =
if (imbalances < 0) false
else cs match {
case Nil => imbalances == 0
case x :: xs =>
if (x == '(') balance(xs, imbalances + 1)
else if (x == ')') balance(xs, imbalances - 1)
else balance(xs, imbalances)
}
balance(chars)
}
println(isBalanced("()".toList)) // true
println(isBalanced("()(())".toList)) // true
println(isBalanced(")(".toList)) // false
println(isBalanced("(()))".toList)) // false
println(isBalanced("((((()))))".toList)) // true