7

免责声明:在有人说之前:是的,我知道这是不好的风格,不被鼓励。我这样做只是为了使用 Scala 并尝试了解更多关于类型推断系统如何工作以及如何调整控制流的信息。我不打算在实践中使用此代码。


所以:假设我在一个相当长的函数中,一开始有很多连续的检查,如果它们失败,都应该导致函数返回一些其他值(而不是抛出),否则返回正常值. 我不能returnFunction. 但是我可以模拟吗?有点像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]——这有点痛苦。有什么办法可以解决这个问题吗?

4

2 回答 2

3

这与您的主要问题有点无关,但我认为,一种更有效的方法(不需要抛出异常)来实现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.

EDIT1:是的,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,我们要么返回值,要么继续计算。

EDIT2: Any chance we might get rid of cpsunit?这里的问题是,shift如果if没有else. 编译器拒绝转换UnitUnit @cps[Unit].

于 2011-06-09T02:35:52.873 回答
0

我认为自定义例外是正确的直觉。

于 2011-06-09T01:57:54.483 回答