将Iterable [[A,B]]减少到[A,Iterable [B]]

时间:2012-10-28 21:04:03

标签: scala functional-programming iterable either

我需要将Iterable [[Throwable,String]]减少为[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})
  }

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

3 个答案:

答案 0 :(得分: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类,并且也符合这些要求,但在sequence列表中使用ValidationNEL会收集多个错误,而不是停留在第一:

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

现在我们得到:

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

您还可以在sequenceOption等列表中使用Promise

答案 1 :(得分: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))

答案 2 :(得分: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
  })