8

我需要将 Iterable[Either[Throwable, String]] 减少为 Either[Throwable, Iterable[String]]。我不知道这个操作是否很常见,在 Iterable 特征上没有发现任何东西。所以我写了这个函数:

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})
  }

如果不是这样,任何人都可以帮助我找到更好的方法吗?

4

3 回答 3

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 还提供了ValidationNEL一个很像Either并且也符合这些要求的类,但是在 ssequence列表上使用会ValidationNEL收集多个错误,而不是在第一个停止:

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

现在我们得到:

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

您还可以在s、s 等sequence列表中使用。OptionPromise

于 2012-10-28T21:48:57.363 回答
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))
于 2012-10-28T21:43:26.463 回答
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
  })
于 2012-10-28T21:24:24.997 回答