2012-10-28 38 views
8

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

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

谁能帮助我找到一个更好的方式,如果这一块是不是?

+0

要实现转型是有点暧昧。你的输入列表包含例如'Right [String]'的一半和各种不同'Left [Exception]'的一半。您希望将其减少为一个例外或字符串列表。如果有例外,应该采取哪种例外在输入中有十个不同点? –

+0

你说得对。我想只考虑它会隐藏其他的第一个异常(或任何左值),但对我的用例来说是可以接受的。 –

+0

这是http://stackoverflow.com/questions/7230999/how-to-reduce-a-seqeithera-b-to-a-eitherseqa-seqb的副本。 – ziggystar

回答

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,也符合这些要求,但ValidationNEL秒的名单上使用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个列表上使用sequence

+2

实际上它和Akka框架中的Future.sequence非常相似,不是吗? –

+0

@FilippoDeLuca:对,就是说,那个人不如斯卡拉斯那么通用。 –

+0

我还不能完全理解scalaz,但我必须试一试,或者更好,它必须尝试给我:) –

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

'case Left(x)=> return Left(x)'可以缩写为'case l @ Left(_)=> return l' –

+2

@KimStebel yes我认为最初,但是'B'类型的参数结果'Either'是错误的(它需要'Iterable [B]'而不是'B'),所以'Left'是一个不同的'Left'。 –

+0

嗯,是的,这是真的 –

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

喜欢它,谢谢。 –

相关问题