2017-01-27 2 views
4

Weitgehend als akademische Übung, ich bin mit meiner eigenen Implementierung von Either in Java, inspiriert von Haskell either (https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Either.html) experimentierenPartitionieren einer Liste von Eithers in Java

Ich habe eine funktionierende Implementierung, die ich zufrieden bin mit:

Either<String, Integer> either = Either.left(A_STRING); 
    assertThat(either.left(), is(A_STRING)); 
    assertThat(either.isLeft(), is(true)); 
    assertThat(either.isRight(), is(false)); 
    assertThat(either.map(x -> AN_INTEGER, y -> A_LONG), is(Either.left(AN_INTEGER))); 
    assertThat(either.consolidate(x -> A_LONG, y -> ANOTHER_LONG), is(A_LONG)); 

Das Äquivalent von Haskells leftsrights und ist ein Strom zu filtern:

Stream<String> s = streamOfEithers 
    .filter(Either::isLeft) 
    .map(x -> x.left()) 

Das Problem, das ich habe, ist in der Schaffung eines Äquivalents von Haskell partitionEithers.

Mein erster Instinkt war:

streamOfEithers.collect(Collectors.groupingBy(Either::isLeft)) 

Allerdings scheint dies meine Art zu verlieren - es gibt Map<Boolean, Either<? extends Object, ? extends Object>> statt (zum Beispiel) Map<Boolean, Either<String,Integer>.

Natürlich ist das Ergebnis der Partitionierung natürlich nicht zwei Listen von Eithers, sondern zwei Listen, eine von jedem Typ. Java hat kein Pair<T,U> Klasse, aber wenn es vielleicht tat

List<Either<String,Integer>> listOfEithers = ... 
Pair<List<String>,List<Integer>> partitioned = Either.partitionList(listOfEithers); 

Ansonsten ist das beste, das ich es geschafft haben, ist:

Map<Boolean, Either<String,Integer>> partitioned = Either.partitionList(listOfEithers); 

..., die für nicht eine sehr bequeme Struktur ein Klient.

Andere als ein Pair schreiben, oder unter Verwendung eines Pair aus einigen Sammlungen Bibliothek (Guava, nebenbei bemerkt, weigert Pair zu schaffen, wie es „mehr Schaden anrichtet als gut“), wie kann ich partitionEithers ein Äquivalent zu Haskell besten zur Verfügung stellen?

Antwort

2

Sie könnten Ihren eigenen Aggregattyp dafür anstelle von Pair erstellen, speziell zugeschnitten auf Either, die einen Kollektor hat, damit zu gehen.Hier ist eine vereinfachte Implementierung:

class Aggregate<T, U> { 
    List<T> tList = new ArrayList<>(); 
    List<U> uList = new ArrayList<>(); 

    public void add(Either<T, U> e) { 
     if(e.isLeft()) { 
      tList.add(e.left()); 
     } else { 
      uList.add(e.right()); 
     } 
    } 

    public Aggregate<T, U> combine(Aggregate<T, U> other) { 
     tList.addAll(other.tList); 
     uList.addAll(other.uList); 
     return this; // could create a new Aggregate here instead... 
    } 

    public static <T, U> Collector<Either<T,U>, ? , Aggregate<T, U>> partitionEithers() { 
     return Collector.of(Aggregate::new, Aggregate::add, Aggregate::combine); 
    } 
} 

Dann wird das Ergebnis:

Aggregate<String, Integer> result = streamOfEithers.collect(Aggregate.partitionEithers()); 
+0

dies akzeptiert, wie es zu dem gleichen Schluss kommt wie ich - eine eigene Art gebraucht wird - ich ging für 'PartitionedEithers ' und es machte unveränderlich. – slim

0

Ich bin nicht vertraut mit Haskell, so kann ich keine vollständige Antwort auf die Frage geben.

Ich weiß jedoch ein wenig über Java-Generika. Das Problem mit

streamOfEithers.collect(Collectors.groupingBy(Either::isLeft))

Map<Boolean, Either<? extends Object, ? extends Object>>

Rückkehr ist wegen der Typ Löschung. Einfach gesagt, Java Generika sind viel schwächer als die gleiche Art von Logik in vielen anderen Sprachen (z. B. Vorlagen in C++). Here ist eine detailliertere Erklärung dessen, was Generika können und was nicht. Wenn Sie wirklich tief graben möchten, würde ich vorschlagen, here zu suchen.

1

Try this:

Map<Boolean, List<Either<Integer, String>>> collect = list.stream().collect(Collectors.partitioningBy(Either::isLeft)); 

Unter der Annahme, dass Ihr Entweder Implementierung der generische Art Signatur ist Either<L,R> (das, was ich habe mit Beispielimplementierung getestet, sollte es kein Problem mit dem Typ löschen geben, und es wird wie erwartet kompilieren.

Danach können Sie Ihre Pair wie folgt initialisieren:

Pair<List<Integer>, List<String>> result = 
     pair(
      collect.getOrDefault(true, Collections.emptyList()).stream().map(Either::left).collect(toList()), 
      collect.getOrDefault(false, Collections.emptyList()).stream().map(Either::right).collect(toList())); 
Verwandte Themen