将 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 是的,一开始我是这么想的,但是结果Either
的B
类型参数是错误的(它需要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
并且也符合这些要求,但是在ValidationNEL
s 列表上使用sequence
会收集多个错误,而不是在第一个停止:
val zs: List[ValidationNEL[String, Int]] =
List(1.successNel, "1st".failNel, "2nd".failNel)
现在我们得到:
scala> print(zs.sequenceU)
Failure(NonEmptyList(1st, 2nd))
您还可以在Option
s、Promise
s 等列表中使用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>>
有没有办法实现((a -> b) -> b) -> Either a b 类型的函数?