2014年3月20日木曜日

古い FunctionalJava のラムダサンプルを Java 8で書き換えてみた (2)

前の投稿に続いて、Java SE 8 のお試しとして FunctionalJavaサンプルをいじってみる。

今回はフィルタ。
import fj.data.Array;
import static fj.data.Array.array;
import static fj.Show.arrayShow;
import static fj.Show.intShow;

public final class Array_filter {
  public static void main(final String[] args) {
    final Array<Integer> a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    final Array<Integer> b = a.filter({int i => i % 2 == 0});
    arrayShow(intShow).println(b); // {44,22,90,98,1078,6,64,6,42}
  }
}
書きなおしたやつがこれ。
import java.util.stream.IntStream;
import static fj.data.Array.iterableArray;
import static fj.Show.arrayShow;
import static fj.Show.intShow;

public final class Array_filter {
  public static void main(final String[] args) {
    final IntStream a = IntStream.of(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    final IntStream b = a.filter(i -> i % 2 == 0);
    arrayShow(intShow).println(iterableArray(()->b.iterator())); // {44,22,90,98,1078,6,64,6,42}
  }
}
あまり変わらないが、Arrayが IntStreamになった他に、当然ながら Show.println()への渡し方も変わってくる。

この Show は Haskell の Show にインスパイアされたのか、文字列化したり、このサンプルみたいに標準出力に書き出したりするクラス。で、arrayShow() から返される Show オブジェクトの println() メソッドは Array を期待しているので、それに渡すために IntStream b から Array を生成したいんだけど、上記のように書ける。

ポイントは Array を生成するための iterableArray() が入力として Iterable を期待している一方で、IntStream には Iterator を返すメソッドはあっても Iterable を返すメソッドはないという点で、どうするのかしばし迷ったが、よく考えると何のことはなくラムダ式で即決できる問題だった。 以下のように書いたのと同じことになる。
Iterable<Integer> iterable = ()-> b.iterator();
arrayShow(intShow).println(iterableArray(iterable)); // {44,22,90,98,1078,6,64,6,42}


ついでにもう1題やってみる。畳み込み。(オリジナルはここ)
  public static void main(final String[] args) {
    final IntStream a = IntStream.of(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    final int b = a.reduce(0, (i, j) -> i + j);
    System.out.println(b); // 1774
  }
実は、IntStream は加算の単位元と二項演算が決まっているので、以下のようにも書ける。
  public static void main(final String[] args) {
    final IntStream a = IntStream.of(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    System.out.println(a.sum()); // 1774
  }
単位元と二項演算が決まっているってことはモノイドが決まっているってことだけど、Monoid クラス自体は、FunctionalJavaにはあるが、Java8にはない。このあたり、FunctionalJava 的なアプローチを Java8 で実装する場合、上記の Show クラスに加えて自前でトライする価値があるかもしれない。

ちなみに以下のように書くと、ストリームが閉じていると言って実行時に怒られる。当然ながらコレクションとストリームでは全然別物ということになる。
  public static void main(final String[] args) {
    final IntStream a = IntStream.of(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    final int b = a.reduce(0, (i, j) -> i + j);
    System.out.println(b); // 1774
    System.out.println(a.sum());
  }

0 件のコメント:

コメントを投稿