2012-10-28 36 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

喜歡它,謝謝。 –

相關問題