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)
定义无限循环的功能方式是递归:
@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)
elbowich的retry无内功能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)
遗憾的是,编译器不够聪明,无法知道你不能退出while循环.但是,即使您无法合理地生成返回类型的成员,也很容易欺骗 - 只是抛出异常.
def process(): String = {
while (true) {
...
}
throw new Exception("How did I end up here?")
}
Run Code Online (Sandbox Code Playgroud)
现在编译器会意识到即使它转义了while循环,它也不能在那里返回一个值,所以它不担心while循环有返回类型Unit(即不返回值).
基于Senia、Buckich和Dave的解决方案,我使用了以下解决方案:
@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)
| 归档时间: |
|
| 查看次数: |
9307 次 |
| 最近记录: |