2014年3月21日金曜日

Java8 で関数の部分適用を書いてみた

取り急ぎ Java8で、引数3つまでの部分適用メソッドを持つ関数クラスを書いてみる。

こんな感じになった。
import java.util.function.Function;
import java.util.function.BiFunction;

interface F1<T, R> extends Function<T, R> {
}
interface F2<T, U, R> extends BiFunction<T, U, R> {
  default F1<U, R> apply(T p1) {
    return p2 -> apply(p1, p2);
  }
}
interface F3<T, U, V, R> {
  R apply(T t, U u, V v);
  default F2<U, V, R> apply(T p1) {
    return (p2, p3) -> apply(p1, p2, p3);
  }
}
public final class PartialTest {

  public static void main(final String[] args) {
    F3<Integer, String, Integer, Integer> f3 =
      (n, s, i) -> String.format("%d%s", n, s).length() + i;
    System.out.println(f3.apply(100, "test", 1)); // 8

    F2<String, Integer, Integer> f2 = f3.apply(1000);
    System.out.println(f2.apply("test", 2)); // 10

    F1<Integer, Integer> f1 = f2.apply("abcde");
    System.out.println(f1.apply(3)); // 12

    System.out.println(
      f3.apply(777).apply("ab").apply(12340));// 12345
  }
}
  • Java8 標準の関数インターフェイスとぜんぜん別系統にしても良かったが、ここでは extends させた。例えば、すでに Function/BiFunctionを使っているアプリケーションに追加するような場合、こうした方が既存コードとの相性が良いと思う。
  • F1、F2、F3 って型名は、「単語は省略すべきじゃない」って原則に反するけど、意味は自明だしこだわらないことにした。そもそも、このレベルの抽象度の高さだとほとんど具体性とか問われないと思うし。FunctionalJava もこんな感じだった。
  • apply() も、もっと抽象的で記号っぽいメソッド名でもよかったけど、ここは Function の既存メソッドに合わせた。
  • F1 は Functionと同じだけど、F2、F3と合わせるために敢えて extends した。Function<T, R> が持っている R apply(T) をそのまま用いる。
  • F2 は BiFunction を extends した。ただし、BiFunction<T, U, R> には、R apply(T, U) があるが、部分適用がないので default メソッドとして定義した。
  • F3 は 適当な基底インターフェイスが無いので、全引数を与えて最終的な値を得るメソッドと、第一引数だけ部分適用するメソッドを持つクラスを作った。
これに似たコードを Java 6とかで書いてみた事があるけど、やはり Java8だとだいぶスッキリする。

同じ要領で F4以上でも書ける(「パラメータが多すぎる関数っていかがなものか」ってのは別にすれば)。

0 件のコメント:

コメントを投稿