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

时间:2012-08-22 09:37:53

标签: scala loops

有些算法执行一个带有条件为true的while循环,并且(肯定)会在某个时刻以while循环体内的return语句结束。 E.g:

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

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

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
}

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

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

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

4 个答案:

答案 0 :(得分:13)

你可以抛出异常:

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

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

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

答案 1 :(得分: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)
}

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

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

请注意,这种方式要求您将函数调用为foo()而不是foo,因为它有一个参数。

答案 2 :(得分:3)

更惯用的方法是使用递归。像这样:

  def foo: Int = { 
    import scala.annotation.tailrec
    @tailrec def whileUnderTen(i: Int):Int = if ( i < 10) whileUnderTen(i+1) else 0
    whileUnderTen(0)
  }

答案 3 :(得分:1)

对于这些场合,我在我的个人标准库中定义了“永久”构造。

forever{
}

在各方面都等同于

while(true){
}

除了forever的类型为Nothing,而等效的while结构的类型为Unit。只是其中一个小扩展功能,使Scala如此高兴。