7

免責事項:誰かがそれを言う前に: はい、私はそれが悪いスタイルであり、奨励されていないことを知っています. これは、Scala をいじって、型推論システムのしくみと制御フローの微調整方法について詳しく学ぼうとしているだけです。このコードを実際に使用するつもりはありません。


だから:私がかなり長い関数にいると仮定してください。最初に多くの連続したチェックがあり、それらが失敗した場合、関数はすべて他の値を返し(スローではありません)、それ以外の場合は通常の値を返します。 . returnの本体には使用できませんFunction。でもシミュレートしていいの?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内でなしでは許可されないことです。コンパイラは への変換を拒否します。ifelseUnitUnit @cps[Unit]

于 2011-06-09T02:35:52.873 に答える
0

ここでは、カスタム例外が正しい本能だと思います。

于 2011-06-09T01:57:54.483 に答える