昨日の「プログラマのための述語論理」で、暗黙的に“総称的な高階関数”が出現してしまいました。それで今日は、意図的に「Javaで高階関数を扱えるだろうか? 」とやってみました。結論を先に言えば、ちょっとシンドい。
関数オブジェクト
まずは、関数オブジェクトを次のインターフェースで定義します。
public interface Function<X, Y> { Y apply(X x); }
Functionf(x) の代わりに f.apply(x) と書く必要がありますが、我慢しましょう。
2引数関数はというと、次のインターフェースを使う手があります。
public interface FunctionOfArity2<X1, X2, Y> { Y apply(X1 x1, X2 x2); }
が、今回はペア(長さ2のタプル)を使いましょう。
public class Pair<X1, X2> { private X1 _1; private X2 _2; public Pair(X1 x1, X2 x2) { _1 = x1; _2 = x2; } public X1 first() {return _1;} public X2 second() {return _2;} }
(X1, X2→Y)という型の関数は、Function<Pair<X1, X2>, Y>型だとみなします。
実例:足し算関数
毎度お馴染みの足し算する関数を定義して実行してみます。
public class Sum { public static void main(String[] args) { Function<Pair<Integer, Integer>, Integer> sum = new Function<Pair<Integer, Integer>, Integer>() { public Integer apply(Pair<Integer, Integer> arg) { return arg.first() + arg.second(); // ここが本体 } }; // y = 5 + 3; int y = sum.apply(new Pair<Integer, Integer>(5, 3)); System.out.println("y = " + y); } }
C:\tmp>java Sum y = 8
それでカリー化
これまたお決まりのカリー化、いってみよう。(JavaScriptのカリー化、「ハラワタを取り出して改変してからもう一度突っこむ」というブラックジャック式にえぐい/ださい方法が→http://d.hatena.ne.jp/m-hiyama/20051213/1134446855にあります。)
public class Curry<X, Y, Z> implements Function<Function<Pair<X, Y>, Z>, Function<X, Function<Y, Z>>> { public Function<X, Function<Y, Z>> apply(final Function<Pair<X, Y>, Z> func) { return new Function<X, Function<Y, Z>>() { public Function<Y, Z> apply(final X x) { return new Function<Y, Z>() { public Z apply(Y y) { return func.apply(new Pair<X, Y>(x, y)); } }; } }; } }
ウニョニョ、なんじゃこりゃー。ほんとにカリー化になっているの?
public class CurrySample { public static void main(String[] args) { // 最初の例 // curry(sum)(5)(3) = 8 Function<Pair<Integer, Integer>, Integer> sum = new Function<Pair<Integer, Integer>, Integer>() { public Integer apply(Pair<Integer, Integer> arg) { return arg.first() + arg.second(); } }; Curry<Integer, Integer, Integer> int_int_int_curry = new Curry<Integer, Integer, Integer>(); Function<Integer, Integer> sum5 = (int_int_int_curry.apply(sum)).apply(5); int y = sum5.apply(3); System.out.println("y = " + y); // y = 5 + 3 = 8 // もうひとつの例 // curry(repeat)("Hi")(4) = "HiHiHiHi" Function<Pair<String, Integer>, String> repeat = new Function<Pair<String, Integer>, String>() { public String apply(Pair<String, Integer> arg) { String s = ""; String x = arg.first(); for (int i = 0; i < arg.second(); i++) { s = s + x; } return s; } }; Curry<String, Integer, String> str_int_str_curry = new Curry<String, Integer, String>(); Function<Integer, String> repeatHi = (str_int_str_curry.apply(repeat)).apply("Hi"); System.out.println(repeatHi.apply(4)); // HiHiHiHi } }
C:\tmp>java CurrySample y = 8 HiHiHiHi
なっ、なってるみたいです。
それがどうした
別にどうもしません。やってみただけ。特にお勧めはしません。