将 Iterable[Either[A,B]] 减少为 Either[A, Iterable[B]]

Posted

技术标签:

【中文标题】将 Iterable[Either[A,B]] 减少为 Either[A, Iterable[B]]【英文标题】:Reducing Iterable[Either[A,B]] to Either[A, Iterable[B]] 【发布时间】:2012-10-18 06:12:18 【问题描述】:

我需要将 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.collectcase Right(y)=> y)
  

如果不是这个,谁能帮我找到更好的方法?

【问题讨论】:

你想要实现的转换有点模棱两可。例如,您的输入列表包含一半的Right[String]s 和一半的各种和异构的Left[Exception]s。您想将其减少为一个异常或字符串列表。如果有例如应该采取哪个例外输入有十个不同? 你是对的。我只想考虑第一个异常(或任何 Left 值),它将隐藏其他异常,但我的用例可以接受。 这是***.com/questions/7230999/…的副本。 【参考方案1】:

我总是觉得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
  )

【讨论】:

case Left(x) => return Left(x) 可以简写为case l@Left(_) => return l @KimStebel 是的,一开始我是这么想的,但是结果EitherB 类型参数是错误的(它需要Iterable[B],但是是B),所以@987654329 @ 是不同的Left 谢谢,但我不喜欢使用 return 语句。 我认为这是使用return 的一个很好的例子(不喜欢return 不应该成为教条)。迭代地处理某些事情,但提前保释并返回一些特殊情况。这就像一个收集 collectFirst(尽管名称并不真正收集),它确实在其实现中使用了 return【参考方案2】:

如果您不喜欢显式返回并希望在稍微缩短代码的同时消除模式匹配,这里是另一个版本:

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

【讨论】:

【参考方案3】:

此操作通常称为排序,在某些函数式语言(如 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 并且也符合这些要求,但是在ValidationNELs 列表上使用sequence 会收集多个错误,而不是在第一个停止:

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

现在我们得到:

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

您还可以在Options、Promises 等列表中使用sequence

【讨论】:

其实和Akka框架中的Future.sequence很像吧? @FilippoDeLuca:是的,确切地说,它没有 Scalaz 的通用。 我还不能完全理解scalaz,但我确实必须尝试一下,或者更好,它必须给我一个尝试:) @FilippoDeLuca:我自己绝对不完全理解 Scalaz!它的好处之一是您不需要 — 只需选择一些起点并四处走动。

以上是关于将 Iterable[Either[A,B]] 减少为 Either[A, Iterable[B]]的主要内容,如果未能解决你的问题,请参考以下文章

Arrow-kt:如何将 Either<E, List<Either<E,A>>> 变成 Either<E, List<B>>

处理 Option 和 Either 类型 - 惯用转换?

05.either 方法

有没有办法实现((a -> b) -> b) -> Either a b 类型的函数?

运算符重载must take either zero or one argument错误

ECMAscript6——iterable