2012-10-28 14 views
8

Iterable [Throwable、String]のいずれかをThrowable、Iterable [String]に減らす必要があります。この操作がかなり一般的かどうかはわかりませんが、Iterable特性には何も見つかりませんでした。だから私は、この機能を書かれている:Iterable [A、B]のいずれかに[A、Iterable [B]]のいずれかを返す

def reduce[A, B](xs: Iterable[Either[A, B]]): Either[A, Iterable[B]] = 
    xs.collectFirst { 
    case Left(x) => x 
    } match { 
    case Some(x) => Left(x) 
    case None => Right(xs.collect{case Right(y)=> y}) 
    } 

誰もがこの1つはそれがないなら、私はより良い方法を見つけるのを助けることができますか?

+0

など、Option秒のリストにPromisesequenceを使用することができます。あなたの入力リストには例えば 'Right [String]'の半分と異種の 'Left [Exception]'の半分が含まれています。例外を1つまたは文字列のリストに減らしたいとします。例えば、ある場合にはどの例外を取るべきですか?入力の10異なる? –

+0

あなたは正しいです。私は、他のものを隠す最初の例外(または左の値)のみを考慮したいと思いますが、私の使用例では受け入れられます。 –

+0

これはhttp://stackoverflow.com/questions/7230999/how-to-reduce-a-seqeithera-b-to-a-eitherseqa-seqbの複製です。 – ziggystar

答えて

11

この操作は、多くの場合、シングと呼ばれ、(例えばHaskellのような)いくつかの関数型言語の標準ライブラリで提供されています。 Scalaでは独自に実装することも、Scalazのような外部ライブラリを使用することもできます。我々は、例えば、以下があるとします。

val xs: List[Either[String, Int]] = List(Right(1), Right(2)) 
val ys: List[Either[String, Int]] = List(Right(1), Left("1st!"), Left("2nd!")) 

今は(Scalaz 7を使用して)作成することができ:

scala> import scalaz._, Scalaz._ 
import scalaz._ 
import Scalaz._ 

scala> xs.sequenceU 
res0: Either[String,List[Int]] = Right(List(1, 2)) 

scala> ys.sequenceU 
res1: Either[String,List[Int]] = Left(1st!) 

所望されます。サイドノートとして


、この操作は、ちょうど外側容器がトラバースと内部容器は応用的ファンクタであることがあることを必要とします。 ScalazもたくさんEitherようなものだとも、これらの要件に適合ValidationNELクラスを提供しますが、ValidationNEL秒のリストにsequenceを使用する代わりに、最初で停止の複数のエラーを収集します。

val zs: List[ValidationNEL[String, Int]] = 
    List(1.successNel, "1st".failNel, "2nd".failNel) 

は今我々が得る:

scala> print(zs.sequenceU) 
Failure(NonEmptyList(1st, 2nd)) 

また、あなたが達成したい変換が少し曖昧である

+2

実際、Akkaフレームワークの 'Future.sequence'と非常に似ていますね。 –

+0

@FilippoDeLuca:まあまあ、それはScalazのものより一般的ではない。 –

+0

私はまだスカラズを完全に理解することはできませんが、実際にそれを試してみてください。それ以上は試してみなければなりません:) –

2

私はいつもreturn少しぎこちない文が、これ以下の作品を見つける:

def reduce[A, B](xs: Iterable[Either[A, B]]): Either[A, Iterable[B]] = 
    Right(xs.collect { 
    case Left(x) => return Left(x) 
    case Right(x) => x 
    }) 
+1

'case left(x)=> return Left(x)'を 'case l @ Left(_)=> return l'に短縮することができます –

+2

@KimStebelはい私は最初に考えましたが、結果的に 'Either'は間違っています(' Iterable [B] 'が必要ですが代わりに' B')ので、 'Left'は異なる' Left'です。 –

+0

ああはい、それは本当です –

4

明示的なリターンを好む、ややコードを短縮しながら、パターンマッチングを排除したくない場合は、ここで別のですバージョン:

def reduce[A, B](xs: Iterable[Either[A, B]]): Either[A, Iterable[B]] = 
    xs collectFirst { 
    case Left(x) => Left(x) 
    } getOrElse Right(xs.flatMap(_.right.toOption)) 
+0

よう、ありがとうございます。 –

関連する問題