2017-01-27 23 views
4

主要由於一項學術活動,我有我自己的實現在Java的Either,靈感來自實驗Haskell的eitherhttps://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Either.html分區中的Java Eithers列表

我有一個工作的實施,我很高興用:

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

Haskell的lefts的等效和rights是過濾流:

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

我遇到的問題是創建一個相當於Haskell的partitionEithers

我的第一反應是:

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

然而,這似乎失去了我的類型 - 它返回Map<Boolean, Either<? extends Object, ? extends Object>>,而不是(例如)Map<Boolean, Either<String,Integer>

當然,理想情況下,分區的結果不是兩個列表Eithers,而是兩個列表,每個列表中的一個。 Java沒有一個Pair<T,U>類,但如果它沒有,也許是:

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

否則,我已成功最好的是:

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

...這是不是一個非常方便的結構一個客戶。

除了一些藏庫編寫Pair,或使用Pair(番石榴,順便說一句,拒絕提供Pair,因爲它「確實弊大於利」),我如何能夠最好地提供等同於Haskell的partitionEithers

回答

2

你可以爲此創建自己的聚合類型,而不是Pair,專門爲Either定製,它有一個收藏家隨它一起去。下面是一個簡單的實現:

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

那麼結果就變成了:

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

接受這是因爲我當時談到了同樣的結論 - 需要自定義類型 - 我去'PartitionedEithers ',並使它不可改變的。 – slim

0

我對Haskell不熟悉,所以我不能提供問題的完整答案。

但是我確實知道一些關於Java泛型的知識。與

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

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

的問題是,因爲類型擦除。簡而言之,Java泛型在許多其他語言(例如,C++中的模板)中比相同類型的邏輯弱得多。 Here是關於泛型可以和不可以做的更詳細的解釋。如果你想深入挖掘,我會建議看看here

1

試試這個:

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

假設你的任何一種實現的泛型類型簽名是Either<L,R>(這是我已經通過示例實現進行了測試),類型擦除應該沒有問題,並且會按預期進行編譯。

之後,就可以初始化你Pair這樣的:

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()));