Javaでそれを引き出す方法があるかどうか疑問に思っていました。クロージャーのネイティブサポートがなければ不可能だと思います。
Java 8(2014年3月18日リリース)はカリー化をサポートします。例Java missingfaktorによる回答 に投稿されたコード)は、次のように書き換えることができます。
import Java.util.function.*;
import static Java.lang.System.out;
// Tested with JDK 1.8.0-ea-b75
public class CurryingAndPartialFunctionApplication
{
public static void main(String[] args)
{
IntBinaryOperator simpleAdd = (a, b) -> a + b;
IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
// Demonstrating simple add:
out.println(simpleAdd.applyAsInt(4, 5));
// Demonstrating curried add:
out.println(curriedAdd.apply(4).applyAsInt(5));
// Curried version lets you perform partial application:
IntUnaryOperator adder5 = curriedAdd.apply(5);
out.println(adder5.applyAsInt(4));
out.println(adder5.applyAsInt(6));
}
}
...これはかなりいいです。個人的には、Java 8が利用可能です。ScalaまたはClojure。もちろん、他の言語機能を提供しますが、それだけでは、移行コストと弱いIDE /ツール/ライブラリサポート、IMOを正当化するには不十分です。
カリー化と部分的なアプリケーションはJavaで絶対に可能ですが、必要なコードの量はおそらくあなたをオフにします。
Javaでのカリー化と部分的なアプリケーションを示すコード:
interface Function1<A, B> {
public B apply(final A a);
}
interface Function2<A, B, C> {
public C apply(final A a, final B b);
}
class Main {
public static Function2<Integer, Integer, Integer> simpleAdd =
new Function2<Integer, Integer, Integer>() {
public Integer apply(final Integer a, final Integer b) {
return a + b;
}
};
public static Function1<Integer, Function1<Integer, Integer>> curriedAdd =
new Function1<Integer, Function1<Integer, Integer>>() {
public Function1<Integer, Integer> apply(final Integer a) {
return new Function1<Integer, Integer>() {
public Integer apply(final Integer b) {
return a + b;
}
};
}
};
public static void main(String[] args) {
// Demonstrating simple `add`
System.out.println(simpleAdd.apply(4, 5));
// Demonstrating curried `add`
System.out.println(curriedAdd.apply(4).apply(5));
// Curried version lets you perform partial application
// as demonstrated below.
Function1<Integer, Integer> adder5 = curriedAdd.apply(5);
System.out.println(adder5.apply(4));
System.out.println(adder5.apply(6));
}
}
FWIWは、上記のHaskellに相当するJavaコード:
simpleAdd :: (Int, Int) -> Int
simpleAdd (a, b) = a + b
curriedAdd :: Int -> Int -> Int
curriedAdd a b = a + b
main = do
-- Demonstrating simpleAdd
print $ simpleAdd (5, 4)
-- Demonstrating curriedAdd
print $ curriedAdd 5 4
-- Demostrating partial application
let adder5 = curriedAdd 5 in do
print $ adder5 6
print $ adder5 9
Java 8. JavaslangとjOOλの両方の関数型は、すぐにカリー化を提供します(これはJDKでの見落としだったと思います)、および CyclopsFunctions module には、JDK関数とメソッド参照をカリー化するための静的メソッドのセットがあります。
Curry.curry4(this::four).apply(3).apply(2).apply("three").apply("4");
public String four(Integer a,Integer b,String name,String postfix){
return name + (a*b) + postfix;
}
「Currying」は消費者にも利用可能です。たとえば、3つのパラメーターを持つメソッドを返し、既に適用されているパラメーターのうちの2つは、これに似た処理を行います
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
[〜#〜] edit [〜#〜]:2014およびJava 8の関数型プログラミング= Javaは、可能性があるだけでなく、見苦しくもありません(美しいと言うことを敢えて)。たとえば、 Rogerioの答え を参照してください。
古い答え:
関数型プログラミング手法を使用する場合、Javaは最良の選択ではありません。 missingfaktorが書いたように、あなたは望むものを達成するためにかなりの量のコードを書かなければなりません。
一方、JVMではJavaに制限されていません-機能言語である Scala または Clojure を使用できます(Scalaは、実際には、機能的およびオブジェクト指向の両方)。
Curryingはfunctionを返す必要があります。これはJava(関数ポインタなし)では不可能ですが、関数メソッドを含む型を定義して返すことができます。
public interface Function<X,Z> { // intention: f(X) -> Z
public Z f(X x);
}
それでは、curry簡単な除算にしましょう。 Dividerが必要です。
// f(X) -> Z
public class Divider implements Function<Double, Double> {
private double divisor;
public Divider(double divisor) {this.divisor = divisor;}
@Override
public Double f(Double x) {
return x/divisor;
}
}
およびDivideFunction:
// f(x) -> g
public class DivideFunction implements Function<Double, Function<Double, Double>> {
@Override
public function<Double, Double> f(Double x) {
return new Divider(x);
}
これで、カリー化された区分を行うことができます。
DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5
さて、Scala、ClojureまたはHaskell(または他の関数型プログラミング言語...)は間違いなくTHE言語カレーやその他の機能的なトリックに使用します。
そうは言っても、Javaを使用して、予想されるボイラープレートの量を増やすことなく、カリー化することは確かに可能です(ただし、型について明示する必要がたくさんありますが、curried
を見てください例;-))。
以下のテストは、両方を示しています。curryinga Function3
into Function1 => Function1 => Function1
:
@Test
public void shouldCurryFunction() throws Exception {
// given
Function3<Integer, Integer, Integer, Integer> func = (a, b, c) -> a + b + c;
// when
Function<Integer, Function<Integer, Function<Integer, Integer>>> cur = curried(func);
// then
Function<Integer, Function<Integer, Integer>> step1 = cur.apply(1);
Function<Integer, Integer> step2 = step1.apply(2);
Integer result = step2.apply(3);
assertThat(result).isEqualTo(6);
}
partial applicationと同様に、この例では実際にタイプセーフではありませんが:
@Test
public void shouldCurryOneArgument() throws Exception {
// given
Function3<Integer, Integer, Integer, Integer> adding = (a, b, c) -> a + b + c;
// when
Function2<Integer, Integer, Integer> curried = applyPartial(adding, _, _, put(1));
// then
Integer got = curried.apply(0, 0);
assertThat(got).isEqualTo(1);
}
これは、「退屈しているから」明日1時間でJavaOneの前に楽しみのために実装した概念実証から取得したものです;-)コードはここにあります: https://github.com/ktoso/ jcurry
一般的なアイデアは比較的簡単にFunctionN => FunctionMに拡張できますが、「本当の型安全性」はパルティアアプリケーションの例では依然として問題であり、カリー化の例ではの大量のボイラープラティコードが必要になりますjcurry、しかしそれは実行可能です。
全体として、実行可能ですが、Scalaそれは箱から出しています;-)
Java 7 MethodHandles: http://www.tutorials.de/threads/Java-7-currying-mit-methodhandles.392397/ でカリー化をエミュレートできます。
import Java.lang.invoke.MethodHandle;
import Java.lang.invoke.MethodHandles;
import Java.lang.invoke.MethodType;
public class MethodHandleCurryingExample {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle sum = lookup.findStatic(Integer.class, "sum", MethodType.methodType(int.class, new Class[]{int.class, int.class}));
//Currying
MethodHandle plus1 = MethodHandles.insertArguments(sum,0,1);
int result = (int) plus1.invokeExact(2);
System.out.println(result); // Output: 3
}
}
Java 8つの可能性をもう1つ取り上げます。
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
Function<Integer, Integer> increment = y -> add.apply(1, y);
assert increment.apply(5) == 6;
次のようなユーティリティメソッドを定義することもできます。
static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
return a2 -> f.apply(a1, a2);
}
これにより、間違いなく読みやすい構文が得られます。
Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
はい、ご自身のコード例をご覧ください。
import Java.util.function.Function;
public class Currying {
private static Function<Integer, Function<Integer,Integer>> curriedAdd = a -> b -> a+b ;
public static void main(String[] args) {
//see partial application of parameters
Function<Integer,Integer> curried = curriedAdd.apply(5);
//This partial applied function can be later used as
System.out.println("ans of curried add by partial application: "+ curried.apply(6));
// ans is 11
//JS example of curriedAdd(1)(3)
System.out.println("ans of curried add: "+ curriedAdd.apply(1).apply(3));
// ans is 4
}
}
これは、curriedAddが別の関数を返すカリー化された関数である単純な例です。これはcurriedに保存されているパラメーターの部分適用に使用できますそれ自体が機能です。これは、画面に印刷するときに後で完全に適用されます。
さらに、後で、JSスタイルのようにそれをどのように使用できるかを見ることができます。
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
Javaでメソッドをカリー化することは常に可能ですが、標準的な方法ではサポートされていません。これを達成しようとすると複雑になり、コードが非常に読みにくくなります。 Javaはこれに適した言語ではありません。
別の選択肢はJava 6+
abstract class CurFun<Out> {
private Out result;
private boolean ready = false;
public boolean isReady() {
return ready;
}
public Out getResult() {
return result;
}
protected void setResult(Out result) {
if (isReady()) {
return;
}
ready = true;
this.result = result;
}
protected CurFun<Out> getReadyCurFun() {
final Out finalResult = getResult();
return new CurFun<Out>() {
@Override
public boolean isReady() {
return true;
}
@Override
protected CurFun<Out> apply(Object value) {
return getReadyCurFun();
}
@Override
public Out getResult() {
return finalResult;
}
};
}
protected abstract CurFun<Out> apply(final Object value);
}
この方法でカレーを達成できます
CurFun<String> curFun = new CurFun<String>() {
@Override
protected CurFun<String> apply(final Object value1) {
return new CurFun<String>() {
@Override
protected CurFun<String> apply(final Object value2) {
return new CurFun<String>() {
@Override
protected CurFun<String> apply(Object value3) {
setResult(String.format("%s%s%s", value1, value2, value3));
// return null;
return getReadyCurFun();
}
};
}
};
}
};
CurFun<String> recur = curFun.apply("1");
CurFun<String> next = recur;
int i = 2;
while(next != null && (! next.isReady())) {
recur = next;
next = recur.apply(""+i);
i++;
}
// The result would be "123"
String result = recur.getResult();
JavaでCurryingを実行できますが、(サポートされていないため)見苦しいですJavaでは、単純なループと単純な式を使用する方が簡単で高速です。カレーを使用する場所の例を投稿する場合、同じことを行う代替案を提案できます。
これは、Javaのカリー化と部分的なアプリケーションのためのライブラリです。
https://github.com/Ahmed-Adel-Ismail/J-Curry
また、2つのパラメーターを取るメソッドにMap.Entryを渡すなど、メソッドパラメーターへのTuplesおよびMap.Entryの分解もサポートしているため、Entry.getKey()は最初のパラメーターに移動し、Entry.getValue() 2番目のパラメータに行きます
READMEファイルの詳細
Java 8でCurryingを使用する利点は、高次関数を定義してから、一次関数と関数引数を連鎖的でエレガントな方法で渡すことができることです。
微分関数である微積分の例を次に示します。
package math;
import static Java.lang.Math.*;
import Java.util.Optional;
import Java.util.function.*;
public class UnivarDerivative
{
interface Approximation extends Function<Function<Double,Double>,
Function<Double,UnaryOperator<Double>>> {}
public static void main(String[] args)
{
Approximation derivative = f->h->x->(f.apply(x+h)-f.apply(x))/h;
double h=0.00001f;
Optional<Double> d1=Optional.of(derivative.apply(x->1/x).apply(h).apply(1.0));
Optional<Double> d2=Optional.of(
derivative.apply(x->(1/sqrt(2*PI))*exp(-0.5*pow(x,2))).apply(h).apply(-0.00001));
d1.ifPresent(System.out::println); //prints -0.9999900000988401
d2.ifPresent(System.out::println); //prints 1.994710003159016E-6
}
}
はい、@Jérômeに同意します。Java 8は、Scalaまたは他の関数型プログラミング言語のような標準的な方法ではサポートされていません。
public final class Currying {
private static final Function<String, Consumer<String>> MAILER = (String ipAddress) -> (String message) -> {
System.out.println(message + ":" + ipAddress );
};
//Currying
private static final Consumer<String> LOCAL_MAILER = MAILER.apply("127.0.0.1");
public static void main(String[] args) {
MAILER.apply("127.1.1.2").accept("Hello !!!!");
LOCAL_MAILER.accept("Hello");
}
}