免责声明:在有人说之前:是的,我知道这是不好的风格,不鼓励。我这样做只是为了使用 Scala 并尝试了解更多关于类型推断系统如何工作以及如何调整控制流的信息。我不打算在实践中使用此代码。
所以:假设我在一个相当长的函数中,在开始时有很多连续的检查,如果它们失败,都应该导致函数返回一些其他值(不是抛出),否则返回正常值.我无法使用 return
在 Function
的 body 里.但是我可以模拟吗?有点像break
在 scala.util.control.Breaks
中模拟?
我想出了这个:
object TestMain {
case class EarlyReturnThrowable[T](val thrower: EarlyReturn[T], val value: T) extends ControlThrowable
class EarlyReturn[T] {
def earlyReturn(value: T): Nothing = throw new EarlyReturnThrowable[T](this, value)
}
def withEarlyReturn[U](work: EarlyReturn[U] => U): U = {
val myThrower = new EarlyReturn[U]
try work(myThrower)
catch {
case EarlyReturnThrowable(`myThrower`, value) => value.asInstanceOf[U]
}
}
def main(args: Array[String]) {
val g = withEarlyReturn[Int] { block =>
if (!someCondition)
block.earlyReturn(4)
val foo = precomputeSomething
if (!someOtherCondition(foo))
block.earlyReturn(5)
val bar = normalize(foo)
if (!checkBar(bar))
block.earlyReturn(6)
val baz = bazify(bar)
if (!baz.isOK)
block.earlyReturn(7)
// now the actual, interesting part of the computation happens here
// and I would like to keep it non-nested as it is here
foo + bar + baz + 42 // just a dummy here, but in practice this is longer
}
println(g)
}
}
我在这里的检查显然是虚拟的,但主要的一点是我想避免这样的事情,实际上有趣的代码最终被嵌套太多了,不符合我的口味:
if (!someCondition) 4 else {
val foo = precomputeSomething
if (!someOtherCondition(foo)) 5 else {
val bar = normalize(foo)
if (!checkBar(bar)) 6 else {
val baz = bazify(bar)
if (!baz.isOK) 7 else {
// actual computation
foo + bar + baz + 42
}
}
}
}
我的解决方案在这里工作正常,如果我愿意,我可以提前返回 4 作为返回值。麻烦的是,我必须明确地写出类型参数
[Int]
——这有点痛苦。有什么办法可以解决这个问题吗?
请您参考如下方法:
这与您的主要问题有点无关,但我认为,一种更有效的方法(不需要抛出异常)来实现 return
将涉及延续:
def earlyReturn[T](ret: T): Any @cpsParam[Any, Any] = shift((k: Any => Any) => ret)
def withEarlyReturn[T](f: => T @cpsParam[T, T]): T = reset(f)
def cpsunit: Unit @cps[Any] = ()
def compute(bool: Boolean) = {
val g = withEarlyReturn {
val a = 1
if(bool) earlyReturn(4) else cpsunit
val b = 1
earlyReturn2(4, bool)
val c = 1
if(bool) earlyReturn(4) else cpsunit
a + b + c + 42
}
println(g)
}
这里唯一的问题是您必须明确使用
cpsunit
.
编辑 1:是的,
earlyReturn(4, cond = !checkOK)
可以实现,但不会那么通用和优雅:
def earlyReturn2[T](ret: T, cond: => Boolean): Any @cpsParam[Any, Any] =
shift((k: Any => Any) => if(cond) ret else k())
k
在上面的代码片段中代表了计算的其余部分。取决于
cond
的值,我们要么返回值,要么继续计算。
编辑2:
Any chance we might get rid of cpsunit?
这里的问题是
shift
内
if
没有
else
的声明是不允许的.编译器拒绝转换
Unit
至
Unit @cps[Unit]
.