カリー化
カリー化を調べると、以下のように書いてあります。
カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。
(中略)
カリー化は部分適用と混同されやすい[1][2]。
wikipedia:カリー化
つまり、以下の3つの概念(というのかな?)があるわけですよね。
- 部分適用
- 普通の関数をカリー化したもの
- 最初からカリー化された関数として作られたもの
準備
ためしに作ってみた。
こんなインタフェースがあるとして…。
import java.util.function.BiFunction;
import java.util.function.Function;
interface F1<T, R> extends Function<T, R> {
default R x(T t) {
return apply(t);
}
}
interface F2<T, U, R> extends BiFunction<T, U, R> {
/** 部分適用 */
default F1<U, R> x(T p1) {
return p2 -> apply(p1, p2);
}
/** カーリー化 */
default F1<T, F1<U, R>> currying() {
return p1 -> p2 -> apply(p1, p2);
}
}
interface F3<T, U, V, R> {
R apply(T t, U u, V v);
/** 部分適用 (1項目) */
default F2<U, V, R> x(T p1) {
return (p2, p3) -> apply(p1, p2, p3);
}
/** 部分適用 (2項目) */
default F1<V, R> x(T p1, U p2) {
return p3 -> apply(p1, p2, p3);
}
/** カーリー化 */
default F1<T, F1<U, F1<V, R>>> currying() {
return p1 -> p2 -> p3 -> apply(p1, p2, p3);
}
}
そして、適当な「普通の関数」があるとします。
// 0. 引数が3つの関数を作る
F3<String, String, String, String> f = (s, e, v) -> s + v + e;
部分適用
部分適用とは、以下の 1-1. のような感じ。
// 1-1. 部分適用する
F1<String, String> partialFunction = f.x("[", "]");
// 1-2. 部分適用した関数を使う
String value1 = partialFunction.x("xyz");
System.out.println(value1); // "[xyz]"
普通の関数をカリー化
普通の関数をカリー化するというのは、以下の 2-1. のような感じ。
// 2-1. 普通の関数をカリー化する
F1<String, F1<String, F1<String, String>>> curried = f.currying();
// 2-2. カリー化した関数に部分適用する
F1<String, String> sandwich1 = curried.x("{").x("}");
// 2-3. 部分適用した関数をさらに使う
String value2 = sandwich1.x("abc");
System.out.println(value2); // "{abc}"
最初からカリー化されている関数
最初からカリー化されている関数を作るのは、以下の 3-1. のような感じ。
// 3-1. 最初からカリー化されている関数を作る
F1<String, F1<String, F1<String, String>>> g = s -> e -> v -> s + v + e;
// 3-2. カリー化されている関数を使う
String value3 = g.x("(").x(")").x("123");
System.out.println(value3); // "(123)"