Ich habe mich gefragt, ob es eine Möglichkeit gibt, dies in Java zu erreichen. Ich denke, es ist nicht möglich ohne native Unterstützung für Schließungen.
Ich habe mich gefragt, ob es eine Möglichkeit gibt, dies in Java zu erreichen. Ich denke, es ist nicht möglich ohne native Unterstützung für Schließungen.
Antworten:
Java 8 (veröffentlicht am 18. März 2014) unterstützt das Currying. Der in der Antwort von fehlenden Faktor angegebene Java-Beispielcode kann wie folgt umgeschrieben werden:
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));
}
}
... was ganz nett ist. Persönlich sehe ich mit Java 8 wenig Grund, eine alternative JVM-Sprache wie Scala oder Clojure zu verwenden. Sie bieten natürlich andere Sprachfunktionen, aber das reicht nicht aus, um die Übergangskosten und die schwächere Unterstützung für IDE / Tools / Bibliotheken, IMO, zu rechtfertigen.
(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
Currying und teilweise Anwendung sind in Java absolut möglich, aber die erforderliche Codemenge wird Sie wahrscheinlich ausschalten.
Einige Codes zur Demonstration von Currying und Teilanwendung in 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 hier ist das Haskell-Äquivalent des obigen Java-Codes:
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
Es gibt viele Optionen für das Currying mit Java 8. Die Funktionstypen Javaslang und jOOλ bieten beide Currying out of the box an (ich denke, dies war ein Versehen im JDK), und das Modul Cyclops Functions verfügt über eine Reihe statischer Methoden zum Currying von JDK-Funktionen und Methodenreferenzen. Z.B
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" ist auch für Verbraucher erhältlich. Um beispielsweise eine Methode mit 3 Parametern zurückzugeben, und 2 der bereits angewendeten, machen wir etwas Ähnliches
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
currying
im Curry.curryn
Quellcode.
EDIT : Ab 2014 und Java 8 ist funktionale Programmierung in Java nicht nur möglich, sondern auch nicht hässlich (ich wage es schön zu sagen). Siehe zum Beispiel Rogerios Antwort .
Alte Antwort:
Java ist nicht die beste Wahl, wenn Sie funktionale Programmiertechniken verwenden möchten. Wie der fehlende Faktor geschrieben hat, müssen Sie ziemlich viel Code schreiben, um das zu erreichen, was Sie wollen.
Auf der anderen Seite sind Sie in JVM nicht auf Java beschränkt - Sie können Scala oder Clojure verwenden, die funktionale Sprachen sind (Scala ist in der Tat sowohl funktional als auch OO).
Beim Currying muss eine Funktion zurückgegeben werden . Dies ist mit Java nicht möglich (keine Funktionszeiger), aber wir können einen Typ definieren und zurückgeben, der eine Funktionsmethode enthält:
public interface Function<X,Z> { // intention: f(X) -> Z
public Z f(X x);
}
Lassen Sie uns nun eine einfache Unterteilung vornehmen . Wir brauchen einen Teiler :
// 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;
}
}
und eine 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);
}
Jetzt können wir eine Curry-Division machen:
DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5
Nun, Scala , Clojure oder Haskell (oder jede andere funktionale Programmiersprache ...) sind definitiv DIE Sprachen , die zum Curryen und für andere funktionale Tricks verwendet werden.
Trotzdem ist es sicherlich möglich, mit Java zu curry, ohne die Super-Mengen an Boilerplate, die man erwarten könnte (nun, es tut sehr weh, explizit über die Typen sprechen zu müssen - schauen Sie sich einfach das curried
Beispiel an ;-)).
Die Tests unten beide präsentieren, currying ein Function3
in 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);
}
sowie teilweise Anwendung , obwohl es in diesem Beispiel nicht wirklich typsicher ist:
@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);
}
Dies stammt aus einem Proof Of Concept, den ich morgen in einer Stunde vor JavaOne zum Spaß implementiert habe, "weil mir langweilig war" ;-) Der Code ist hier verfügbar: https://github.com/ktoso/jcurry
Die allgemeine Idee könnte relativ einfach auf FunctionN => FunctionM erweitert werden, obwohl "echte Typensicherheit" ein Problem für das partia-Anwendungsbeispiel bleibt und das Currying-Beispiel eine Menge Boilerplaty-Code in jcurry erfordern würde , aber es ist machbar.
Alles in allem ist es machbar, aber in Scala ist es out of the box ;-)
Mit Java 7 MethodHandles kann man Currying emulieren: 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
}
}
Ja, sehen Sie sich das Codebeispiel an:
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
}
}
Dies ist ein einfaches Beispiel, bei dem curriedAdd eine Curry-Funktion ist, die eine andere Funktion zurückgibt, und dies kann zur teilweisen Anwendung von Parametern verwendet werden, die in curried gespeichert sind, was eine Funktion für sich ist. Dies wird jetzt später vollständig angewendet, wenn wir es auf dem Bildschirm drucken.
Darüber hinaus können Sie später sehen, wie Sie es in einer Art JS-Stil als verwenden können
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
Noch eine Sicht auf die Java 8-Möglichkeiten:
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
Function<Integer, Integer> increment = y -> add.apply(1, y);
assert increment.apply(5) == 6;
Sie können auch Dienstprogrammmethoden wie diese definieren:
static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
return a2 -> f.apply(a1, a2);
}
Was Ihnen eine wohl besser lesbare Syntax gibt:
Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
Das Currying einer Methode ist in Java immer möglich, wird jedoch nicht standardmäßig unterstützt. Der Versuch, dies zu erreichen, ist kompliziert und macht den Code ziemlich unlesbar. Java ist hierfür nicht die geeignete Sprache.
Eine andere Wahl ist hier für 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);
}
dann könnten Sie auf diese Weise Currying erreichen
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();
Während Sie Currying in Java ausführen können, ist es hässlich (weil es nicht unterstützt wird). In Java ist es einfacher und schneller, einfache Schleifen und einfache Ausdrücke zu verwenden. Wenn Sie ein Beispiel für die Verwendung von Curry veröffentlichen, können wir Alternativen vorschlagen, die dasselbe tun.
2 * ?
In Java würden Sie dies mit einer Schleife tun.
Dies ist eine Bibliothek für Currying und Teilanwendung in Java:
https://github.com/Ahmed-Adel-Ismail/J-Curry
Es unterstützt auch die Destrukturierung von Tupeln und Map.Entry in Methodenparameter, z. B. das Übergeben eines Map.Entry an eine Methode, die zwei Parameter akzeptiert, sodass Entry.getKey () zum ersten Parameter und Entry.getValue () wechselt. wird für den zweiten Parameter gehen
Weitere Details in der README-Datei
Der Vorteil der Verwendung von Currying in Java 8 besteht darin, dass Sie Funktionen höherer Ordnung definieren und dann eine Funktion erster Ordnung und Funktionsargumente auf verkettete, elegante Weise übergeben können.
Hier ist ein Beispiel für Calculus, die Ableitungsfunktion.
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
}
}
Ja, ich stimme @ Jérôme zu, Curring in Java 8 wird nicht wie in Scala oder anderen funktionalen Programmiersprachen standardmäßig unterstützt.
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");
}
}