Scala虽然(真)类型不匹配?scala中的无限循环?

TN.*_*TN. 9 loops scala infinite-loop

为什么遵循代码

def doSomething() = "Something"

var availableRetries: Int = 10

def process(): String = {
  while (true) {
    availableRetries -= 1
    try {
      return doSomething()
    } catch {
      case e: Exception => {
        if (availableRetries < 0) {
          throw e
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

产生以下编译错误

error: type mismatch;
 found   : Unit
 required: String
             while (true) {
             ^
Run Code Online (Sandbox Code Playgroud)

这在C#中可以正常工作.while循环永远,所以它不能终止,因此它不能产生除字符串以外的东西.或者如何在Scala中创建无限循环?

Jam*_*Iry 17

与基于语句的语言的C#(以及Java和C和C++)不同,Scala是一种基于表达式的语言.在组合性和可读性方面,这大多是一个很大的优势,但在这种情况下,差异已经让你感到困惑.

Scala方法隐式返回方法中最后一个表达式的值

scala> def id(x : String) = x
id: (x: String)String

scala> id("hello")           
res0: String = hello
Run Code Online (Sandbox Code Playgroud)

在Scala中,几乎所有东西都是表达式.看起来像语句的东西仍然是表达式,它返回一个名为Unit的类型的值.该值可以写为().

scala> def foo() = while(false){}
foo: ()Unit

scala> if (foo() == ()) "yes!" else "no"
res2: java.lang.String = yes!
Run Code Online (Sandbox Code Playgroud)

没有用于图灵等效语言的编译器可以检测所有非终止循环(参见图灵暂停问题),因此大多数编译器几乎不做任何检测工作.在这种情况下,"while(someCondition){...}"的类型是Unit,无论someCondition是什么,即使它是常量true.

scala> def forever() = while(true){}
forever: ()Unit
Run Code Online (Sandbox Code Playgroud)

Scala确定声明的返回类型(String)与实际返回类型(Unit)不兼容,后者是最后一个表达式的类型(while ...)

scala> def wtf() : String = while(true){}
<console>:5: error: type mismatch;
 found   : Unit
 required: String
       def wtf() : String = while(true){}
Run Code Online (Sandbox Code Playgroud)

答:最后添加一个例外

scala> def wtfOk() : String = {
     | while(true){}
     | error("seriously, wtf? how did I get here?")
     | }
wtfOk: ()String
Run Code Online (Sandbox Code Playgroud)

  • 理论上,语言可以有一个特殊情况来识别while(true){...}类型为Nothing,而不是类型Unit.然后该示例将正常工作,因为Nothing是String的子类型(以及其他所有内容).我在几年前的邮件中提出了这个问题,但一般的回答是它不经常发生,不值得改变语言.如果你经常这样做,那么你可以很容易地创建一个"永远"的方法,但它有返回类型Nothing而不是Unit. (3认同)

sen*_*nia 8

定义无限循环的功能方式是递归:

@annotation.tailrec def process(availableRetries: Int): String = {
  try {
    return doSomething()
  } catch {
    case e: Exception => {
      if (availableRetries < 0) {
        throw e
      }
    }
  }
  return process(availableRetries - 1)
}
Run Code Online (Sandbox Code Playgroud)

elbowichretry无内功能loop功能:

import scala.annotation.tailrec 
import scala.util.control.Exception._ 

@tailrec def retry[A](times: Int)(body: => A): Either[Throwable, A] = { 
  allCatch.either(body) match { 
    case Left(_) if times > 1 => retry(times - 1)(body) 
    case x => x 
  } 
} 
Run Code Online (Sandbox Code Playgroud)


Rex*_*err 5

遗憾的是,编译器不够聪明,无法知道你不能退出while循环.但是,即使您无法合理地生成返回类型的成员,也很容易欺骗 - 只是抛出异常.

def process(): String = {
  while (true) {
    ...
  }
  throw new Exception("How did I end up here?")
}
Run Code Online (Sandbox Code Playgroud)

现在编译器会意识到即使它转义了while循环,它也不能在那里返回一个值,所以它不担心while循环有返回类型Unit(即不返回值).


TN.*_*TN. 2

基于SeniaBuckichDave的解决方案,我使用了以下解决方案:

@annotation.tailrec
def retry[T](availableRetries: Int)(action: => T): T = {
  try {
    return action
  } catch {
    case e: Exception if (availableRetries > 0) => { }
  }
  retry(availableRetries - 1)(action)
}
Run Code Online (Sandbox Code Playgroud)

然后可以将其用作肘部和戴夫的解决方案:

retry(3) {
  // some code
}
Run Code Online (Sandbox Code Playgroud)