如何告诉Scala编译器while循环将返回一个值?

Joh*_*ood 2 loops scala

有些算法在条件为true的情况下执行while循环,并且(肯定)在某些时候以while循环体内的return语句结束.例如:

def foo: Int = {
  while(true) {
    // At some time, the while loop will do a return statement inside its body
    if( ... )
      return 0
  }
}
Run Code Online (Sandbox Code Playgroud)

简单的例子(没有语义意义):

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
}
Run Code Online (Sandbox Code Playgroud)

Scala编译器抱怨类型不匹配,因为while循环的类型为Unit而编译器不知道,while循环将在某个时刻返回一个值.我们可以通过以下方法解决此问题:

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  0 // !
}
Run Code Online (Sandbox Code Playgroud)

但这看起来很难看.有更好的解决方法吗?或者甚至是针对这类问题的更好解决方案?

jqn*_*qno 13

你可以抛出异常:

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  throw new IllegalStateException("This should never happen")
}
Run Code Online (Sandbox Code Playgroud)

编译器将停止抱怨类型不匹配,并且由于while循环总是返回某些内容,因此永远不会抛出异常.如果是的话,你会很快发现你做错了什么:).

还有其他方法可以编写这个循环,它更像是idomatic和Scala-esque,但是考虑到你提供的代码,这将以清晰简单的方式完成工作.


Dao*_*Wen 8

也许你应该只使用尾递归.它应该最终编译成非常相似的字节码:

import scala.annotation.tailrec

def foo: Int = {
  @tailrec def bar(i: Int): Int = {
    val j = i + 1
    if (j == 10) return 0
    else bar(j)
  }
  bar(0)
}
Run Code Online (Sandbox Code Playgroud)

您甚至可能希望使用默认参数值支持:

@tailrec def foo(i: Int = 0): Int = {
  val j = i + 1
  if (j == 10) return 0
  else foo(j)
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是这种方式要求您为调用函数foo()不是foo因为它有一个参数.