2016-11-15 11 views
4

私はif/というステートメントは自明ではありません。私はそれぞれを明確な説明名で独自の関数に抽出し、それらの関数を連鎖させたいと思います。Scalaのリファクタリング(if/elsif/elsif)チェーン?

コールチェーンの途中でスカラを停止するにはどうすればよいですか?ここで

コードの例です:

// actual code 

for(klass <- program.classes) { 
    if (complicated boolean) { //checkVars 
     error1 
    } else if (complicated boolean) { //checkMethods 
     error2 
    } else if (...) { //... 
     error3 
    } else { 
     complicated good case code 
    } 
} 

// wanted 

for(klass <- program.classes) { 
    (checkName 
    andThen checkVars 
    andThen checkMethods 
    andThen addToContext) (klass) 
// where the chaining stops if a check fails 
} 
+0

エラーの場合の対処方法は?例外を投げたり、何かを印刷したりして、エラーオブジェクトを出力したいのですか? –

+0

print something –

答えて

3

部分的な結果を抽出しているときは、連鎖検証にfor理解してOption[_]を返すOptionタイプとメソッドを使用することができます。

def checkName(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None 
def checkVars(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None 
def checkMethods(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None 
def finalOp(klass: Klass): OutputClass = //your final operation 

// Use the above checks 
program.classes.map(checkName(_).flatMap(checkVars).flatMap(checkMethods).map(finalOp).getOrElse(defaultResult)) 

したい場合は、次の処理はオプションはなし

for { 
    klass <- program.classes 
    name <- checkName // Option[String] 
    vars <- checkVars // Option[SomeType] 
    methods <- checkMethods // Option[SomeOtherT] 
    ctx <- addToContext // Option[...] 
} { 
// do something with klass 
// if you got here, all the previous Options returned Some(_) 
} 
+0

ありがとうございます。私はそれについて考えましたが、小切手は何も返さないと考えていたので、それは嫌です。彼らはダミーの値を返した場合、コードが理解しやすいだろうか分からない。 + +良いアイデアのために+1 –

+1

私は自分自身の解決策には部分的かもしれませんが、スカラではほとんどのものが何かを返します。なぜなら、そうでなければ副作用があり、それは "The Commandments" 「ベストプラクティス」)。 – radumanolescu

+2

値を抽出したくない場合は、「ユニット」を抽出できます。実際には 'Option(Unit)'には 'Boolean'と同じように' Some(()) 'と' None'の2つの値があり、他のプロパティを調べ続けることができます。 ) '。値(常に '()')を気にしないので、あなたの 'for'ブロックの' < - 'のlhsに' _'を置くことができます。 –

2

を返さないとき、これはあなたがエラーで発生する内容に多くを依存するが、このオプションを使用して連鎖マップのために良い場合のように思える停止しますスキップする/あなたはflatMapを使用することになり、あなたのチェックのすべてを失敗する要素を省略:

program.classes.flatMap(checkName(_).flatMap(checkVars).flatMap(checkMethods).map(finalOp)) 
+0

小切手は値を返すという事実に悩まされましたが、最終的にチェーンはかなり良く見えます。 +1のコードサンプル 'checkName'など...本当に何かチェックから返されたもの以外のオプションはありませんか? –

+1

ええ、私はそれを書いていたときと同じことを考えていました。もしif(compBoolean)Some(klass)else None'はかなり醜いですし、あまり慣れない気がしません。 –

2

フィルタとオプションを使用して、あなたがチェックが簡単にABを返すようにできるようになりますoolean値。すべてのチェックが渡された場合、それらのいずれかが失敗した場合の例

def checkName(klass: Klass): Boolean = ??? 
def checkVars(klass: Klass): Boolean = ??? 
def checkMethods(klass: Klass): Boolean = ??? 
def finalOp(klass: Klass): OutputClass = ??? 

Option(klass) 
    .filter(checkName) 
    .filter(checkVars) 
    .filter(checkMethods) 
    .map(finalOp) 

のためにあなたはNoneSome()が残されます。オプションの倍を使用して

+0

定義された述語関数は読みやすくするためのもので、フィルタ呼び出しの複合条件をラムダとして直接呼び出すことができます。 –

+0

これは私の答えよりもはるかに意味がある –

+0

確かに。私は小切手から 'ブール'を返す考えが好きです。意味あり。 +1 –

0

チェーン

あなたはOptionfoldメソッドを使用することができます。

倍は、任意の一般的な使用の場合にも適用することができる標準ライブラリ

final def fold[B](ifEmpty: => B)(f: Int => B): B 

これで、次のように定義されています。あなたがしなければならないことは、何かのOptionを継続的に返すことだけです。以下の場合、いずれかのメソッドがNoneを返す場合、チェーンブレイクが発生します。以下のコードでは、SomeまたはNoneをメッセージとして送信して次の操作に通信します。

def f1: Option[_] = ??? 
def f2: Option[_] = ??? 
def f3: Option[_] = ??? 

f1.fold[Option[Unit]](None)(_ => f2).fold[Option[Unit]](None)(_ => f3) 

ScalaのREPL

scala> Option(1).fold[Option[Unit]](None)(_ => Some(println("hello"))).fold[Option[Unit]](None)(_ => Some(println("scala"))) 
hello 
scala 
res59: Option[Unit] = Some(()) 

scala> None.fold[Option[Unit]](None)(_ => Some(println("hello"))).fold[Option[Unit]](None)(_ => Some(println("scala"))) 
res60: Option[Unit] = None 

scala> Option(1).fold[Option[Unit]](None)(_ => None).fold[Option[Unit]](None)(_ => Some(println("scala"))) 
res61: Option[Unit] = None 
2
program.classes foreach { 
    case klass if checkName(klass) => error1 
    case klass if checkVars(klass) => error2 
    case klass if checkMethods(klass) => error3 
    case klass => addToContext(klass) 
} 
+1

あなたの答えは短すぎます。どうしてこのようにしなければならないのか、いくつか説明してください。 –

+0

スマート...あまりにもスマートな^^ +1でも面白い考えです。 –

2

(質問が尋ねると)部分関数組成物を用いて、質問に答えるために、我々はPartialFunctionとして各チェックを定義します。結果の型としてTryも使用しています。 Tryは、処理中に発生する特定のエラー情報を保持することができます。 Option)は、要素が見つからなかった理由を保持していないため、実際にエラー情報を気にしない限り、チェックを実装するために使用しません。)

簡略化した例:ここ

import scala.util.{Try, Success, Failure} 

val check1:PartialFunction[Int, Try[String]] = {case x if x==1 => Failure(new Exception("error1"))} 
val check2:PartialFunction[Int, Try[String]] = {case x if x==2 => Failure(new Exception("error2"))} 
val check3:PartialFunction[Int, Try[String]] = {case x if x==3 => Failure(new Exception("error3"))} 
val process: PartialFunction[Int, Try[String]] = {case x => Success(s"[$x] processed OK")} 

val checks = check1 orElse check2 orElse check3 orElse process 

for (i <- 1 to 4) yield (checks(i)) 
// scala.collection.immutable.IndexedSeq[scala.util.Try[String]] = Vector(
// Failure(java.lang.Exception: error1), 
// Failure(java.lang.Exception: error2), 
// Failure(java.lang.Exception: error3), 
// Success([4] processed OK) 
//) 
+0

非常に興味深い(+1)が、私は失敗時ではなく、成功に連鎖する必要があります –

+0

@Julien__このメソッドは成功に連鎖し、失敗時に存在します。プロセスが失敗するまで(数字1〜3)、最終的な 'process'ステートメント(#4)に達するまで、各チェックをどのように渡すかに注意してください。 – maasg

+0

ああ、私は今参照してください。これは、liljerkの答えの "部分的な機能"バージョンです。 'orElse'は通常失敗事例に関連しているので、私はそれが気にしています。しかしここでは、部分的な機能は成功の場合に定義されていません。間接レイヤーがあります。 (「真実」ではなく「偽ではない」というようなものです)。 –

0

もスカラに適用可能である私はCで多く使用されるパターンです。私は今朝それを思い出しました。 &&からthenIfSuccessまたはそれに類するもの(図示せず)の名前を変更する場合は、暗黙のメソッドを作成できます。

&&が2番目の引数で怠惰であるという事実を利用します。


def checkName(klass: Klass): Boolean = ??? 
def checkVars(klass: Klass): Boolean = ??? 
def checkMethods(klass: Klass): Boolean = ??? 
def finalOp(klass: Klass): Boolean = ??? 

// just chain the method calls : 
checkName(cls) && checkVars(cls) && checkMethods(cls) && finalOp(cls) 


// this will call each method in order and stop if one fails. 

あなたが考えてみれば、読みその非常に簡単、foldfilterやパターンマッチングを使用して他の回答よりも多く。 forの表現はまた、imhoを読むのが非常に簡単ですが、非常に自然ではないOption[_]を返すように強制します。