52

エラーメッセージSeq[Either[String,A]]である一連のいずれかが与えられます。シーケンスのLeftすべての要素Either[String,Seq[A]]が. 少なくとも 1 つ(エラー メッセージ) がある場合、最初のエラー メッセージまたはすべてのエラー メッセージの連結を取得したいと考えています。RightSeq[A]RightLeft

もちろん、cats や scalaz のコードを投稿することもできますが、それを使用しないコードにも興味があります。

編集

Either[Seq[A],Seq[B]]メッセージの本文を反映するために最初に要求されたタイトルを変更しました。

4

8 に答える 8

33

編集:あなたの質問のタイトルが尋ねられたことを見逃しましたが、Either[Seq[A],Seq[B]]「最初のエラーメッセージまたはすべてのエラーメッセージの連結を取得したい」を読みました。これにより前者が得られます。

def sequence[A, B](s: Seq[Either[A, B]]): Either[A, Seq[B]] =
  s.foldRight(Right(Nil): Either[A, List[B]]) {
    (e, acc) => for (xs <- acc.right; x <- e.right) yield x :: xs
  }

scala> sequence(List(Right(1), Right(2), Right(3)))
res2: Either[Nothing,Seq[Int]] = Right(List(1, 2, 3))

scala> sequence(List(Right(1), Left("error"), Right(3)))
res3: Either[java.lang.String,Seq[Int]] = Left(error)

Scalazの使用:

val xs: List[Either[String, Int]] = List(Right(1), Right(2), Right(3))

scala> xs.sequenceU
res0:  scala.util.Either[String,List[Int]] = Right(List(1, 2, 3))
于 2011-08-29T14:02:51.293 に答える
16

開始シーケンスが与えられた場合xs、これが私の見解です:

xs collectFirst { case x@Left(_) => x } getOrElse
  Right(xs collect {case Right(x) => x})

これは質問の本文への回答であり、最初のエラーのみをEither[String,Seq[A]]. 明らかに、タイトルの質問に対する有効な回答ではありません


すべてのエラーを返すには:

val lefts = xs collect {case Left(x) => x }
def rights = xs collect {case Right(x) => x}
if(lefts.isEmpty) Right(rights) else Left(lefts)

rightsはメソッドとして定義されているため、必要に応じてオンデマンドでのみ評価されることに注意してください

于 2011-08-29T19:00:07.813 に答える
11

それは動作するはずです:

def unfoldRes[A](x: Seq[Either[String, A]]) = x partition {_.isLeft} match {
  case (Nil, r) => Right(r map {_.right.get})
  case (l, _) => Left(l map {_.left.get} mkString "\n")
}

結果を左と右に分割します。左が空の場合は右を作成し、そうでない場合は左を作成します。

于 2011-08-29T14:02:48.017 に答える
8

スカラーズのコードは次のとおりです。

_.sequence

于 2011-09-15T09:52:48.743 に答える
2

Kevinのソリューションに基づいて構築し、HaskellのEitherタイプから少し盗むと、次のようにメソッドpartitionEithersを作成できます。

def partitionEithers[A, B](es: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
  es.foldRight (Seq.empty[A], Seq.empty[B]) { case (e, (as, bs)) =>
    e.fold (a => (a +: as, bs), b => (as, b +: bs))
  }

そしてそれを使用してソリューションを構築します

def unroll[A, B](es: Seq[Either[A, B]]): Either[Seq[A], Seq[B]] = {
  val (as, bs) = partitionEithers(es)
  if (!as.isEmpty) Left(as) else Right(bs)
}
于 2011-08-29T20:15:53.507 に答える
0

私はどちらかを使用することに慣れていません-これが私のアプローチです。よりエレガントなソリューションがあるかもしれません:

def condense [A] (sesa: Seq [Either [String, A]]): Either [String, Seq [A]] = {
  val l = sesa.find (e => e.isLeft)
  if (l == None) Right (sesa.map (e => e.right.get)) 
  else Left (l.get.left.get)
}

condense (List (Right (3), Right (4), Left ("missing"), Right (2)))
// Either[String,Seq[Int]] = Left(missing)
condense (List (Right (3), Right (4), Right (1), Right (2)))
// Either[String,Seq[Int]] = Right(List(3, 4, 1, 2))

Left (l.get.left.get)少しおかしく見えますが、lそれ自体は [A, B] であり、[A, Seq[B]] ではなく、再ラップが必要です。

于 2011-08-29T15:35:04.020 に答える
0

私の答えは @Garrett Rowe のものに似ています: しかし、それは foldLeft を使用し (また参照:なぜ foldRight と reduceRight は末尾再帰ではありませんか? )、Seq に追加するのではなく、Seq の先頭に追加します (参照:リストに追加するのが悪いのはなぜですか? )。

scala> :paste
// Entering paste mode (ctrl-D to finish)

def partitionEitherSeq[A,B](eitherSeq: Seq[Either[A,B]]): (Seq[A], Seq[B]) =
  eitherSeq.foldLeft(Seq.empty[A], Seq.empty[B]) { (acc, next) =>
  val (lefts, rights) = acc
  next.fold(error => (lefts :+ error, rights), result => (lefts, rights :+ result))
}

// Exiting paste mode, now interpreting.

partitionEitherSeq: [A, B](eitherSeq: Seq[Either[A,B]])(Seq[A], Seq[B])

scala> partitionEitherSeq(Seq(Right("Result1"), Left("Error1"), Right("Result2"), Right("Result3"), Left("Error2")))
res0: (Seq[java.lang.String], Seq[java.lang.String]) = (List(Error1, Error2),List(Result1, Result2, Result3))
于 2014-01-07T23:40:00.623 に答える