2012-10-28 7 views
8

Ich muss ein Iterable [Entweder [Throwable, String]] zu einem Entweder [Throwable, Iterable [String]] reduzieren. Ich weiß nicht, ob diese Operation ziemlich üblich ist oder nicht, habe nichts auf der Iterable Eigenschaft gefunden. Also habe ich diese funktion geschrieben:Reduzieren von Iterable [Entweder [A, B]] zu Entweder [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}) 
    } 

Kann mir jemand helfen, einen besseren Weg zu finden, wenn dieser es nicht ist?

+0

Die Transformation Sie erreichen wollen. Ihre Eingabeliste enthält zum Beispiel die Hälfte von 'Right [String]' s und die Hälfte von verschiedenen und heterogenen 'Left [Exception]' s. Sie möchten es auf eine Ausnahme oder eine Liste von Zeichenfolgen reduzieren. Welche Ausnahme sollte genommen werden, wenn z.B. zehn verschiedene in der Eingabe? –

+0

Sie haben Recht. Ich möchte nur die erste Ausnahme (oder irgendeinen linken Wert) betrachten, die andere verbergen wird, aber es ist akzeptabel für meinen Anwendungsfall. –

+0

Dies ist ein Duplikat von http://stackoverflow.com/questions/7230999/how-to-reduce-a-seqeithera-b-to-a-eitherseqa-seqb. – ziggystar

Antwort

11

Dieser Vorgang oft Sequenzierung genannt wird, und ist in den Standardbibliotheken einiger funktionaler Sprachen (wie Haskell) zur Verfügung. In Scala können Sie entweder eigene implementieren oder eine externe Bibliothek wie Scalaz verwenden. Angenommen, wir haben die folgende, zum Beispiel:

val xs: List[Either[String, Int]] = List(Right(1), Right(2)) 
val ys: List[Either[String, Int]] = List(Right(1), Left("1st!"), Left("2nd!")) 

Jetzt können wir schreiben (mit 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!) 

nach Wunsch.


Als Randbemerkung, diese Operation erfordert nur, dass der Außenbehälter überfahrbar sein und dass der Innenbehälter ein applicative Funktors sein. Scalaz bietet auch eine ValidationNEL Klasse, die viel wie Either ist und passt auch diese Anforderungen, sondern auf einer Liste von ValidationNEL s sammelt sequence unter Verwendung mehrerer Fehler stattdessen beim ersten Anhalten:

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

Jetzt erhalten wir:

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

Sie auch ein bisschen zweideutig verwenden könnte sequence auf einer Liste von Option s, Promise s usw.

+2

In der Tat ist es sehr ähnlich zu "Future.sequence" im Akka-Framework, oder? –

+0

@FilippoDeLuca: Ja, genau, das ist nur weniger allgemein als Scalaz. –

+0

Ich bin noch nicht in der Lage scalaz zu verstehen, aber ich muss es tatsächlich versuchen, oder besser, es muss einen Versuch zu mir geben :) –

2

finde ich immer return Aussagen ein wenig umständlich, aber das folgende Arbeiten:

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 Links (x) => return Links (x)' kann verkürzt werden zu 'case l @ Links (_) => return l' –

+2

@KimStebel ja das dachte ich zuerst, aber der 'B' type parameter von das resultierende 'Entweder' ist falsch (es erfordert' Iterable [B] ', aber ist' B' stattdessen), so dass das 'Left' ein anderes' Left' ist. –

+0

ah ja, das stimmt –

4

Wenn Sie die explizite Rückkehr nicht mögen und wollen die Pattern-Matching zu beseitigen, während Sie den Code ein wenig zu verkürzen, hier ist eine andere Version:

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

Wie es, danke. –

Verwandte Themen