Je me demandais s'il y avait un moyen de tirer cela en Java. Je pense que ce n'est pas possible sans un support natif des fermetures.
Je me demandais s'il y avait un moyen de tirer cela en Java. Je pense que ce n'est pas possible sans un support natif des fermetures.
Réponses:
Java 8 (sorti le 18 mars 2014) prend en charge le curry. L'exemple de code Java publié dans la réponse par missingfaktor peut être réécrit comme suit :
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));
}
}
... ce qui est plutôt sympa. Personnellement, avec Java 8 disponible, je vois peu de raisons d'utiliser un autre langage JVM tel que Scala ou Clojure. Ils fournissent d'autres fonctionnalités de langage, bien sûr, mais cela ne suffit pas pour justifier le coût de transition et le support IDE / outillage / bibliothèques plus faible, IMO.
(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
Le curry et l'application partielle sont tout à fait possibles en Java, mais la quantité de code requise vous désactivera probablement.
Un peu de code pour illustrer le curry et l'application partielle en 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 voici l'équivalent Haskell du code Java ci-dessus:
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
Il existe de nombreuses options pour Currying avec Java 8. Type de fonction Javaslang et jOOλ offrant tous deux Currying prêt à l'emploi (je pense que c'était un oubli dans le JDK), et le module Cyclops Functions a un ensemble de méthodes statiques pour Currying JDK Functions et références de méthode. Par exemple
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» est également disponible pour les consommateurs. Par exemple, pour renvoyer une méthode avec 3 paramètres, et 2 de ceux déjà appliqués, nous faisons quelque chose de similaire à ceci
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
currying
dans le Curry.curryn
code source.
EDIT : Depuis 2014 et Java 8, la programmation fonctionnelle en Java est désormais non seulement possible, mais pas non plus laide (j'ose dire beau). Voir par exemple la réponse de Rogerio .
Ancienne réponse:
Java n'est pas le meilleur choix si vous allez utiliser des techniques de programmation fonctionnelle. Comme l'a écrit missingfaktor, vous devrez écrire une assez grande quantité de code pour obtenir ce que vous voulez.
D'autre part, vous n'êtes pas limité à Java sur JVM - vous pouvez utiliser Scala ou Clojure qui sont des langages fonctionnels (Scala est, en fait, à la fois fonctionnel et OO).
Le curry nécessite de retourner une fonction . Ce n'est pas possible avec java (pas de pointeurs de fonction) mais nous pouvons définir et renvoyer un type qui contient une méthode de fonction:
public interface Function<X,Z> { // intention: f(X) -> Z
public Z f(X x);
}
Maintenant curry une division simple. Nous avons besoin d'un diviseur :
// 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;
}
}
et une fonction de division :
// f(x) -> g
public class DivideFunction implements Function<Double, Function<Double, Double>> {
@Override
public function<Double, Double> f(Double x) {
return new Divider(x);
}
Maintenant, nous pouvons faire une division curry:
DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5
Eh bien, Scala , Clojure ou Haskell (ou tout autre langage de programmation fonctionnel ...) sont définitivement LES langages à utiliser pour le curry et autres astuces fonctionnelles.
Cela dit, il est certainement possible de curry avec Java sans les super quantités de passe-partout à laquelle on pourrait s'attendre (enfin, devoir être explicite sur les types fait beaucoup mal - jetez simplement un coup d'œil à l' curried
exemple ;-)).
Les tests ci-dessous présentent les deux, en curry un Function3
en 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);
}
ainsi qu'une application partielle , bien que ce ne soit pas vraiment sécurisé dans cet exemple:
@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);
}
Ceci est tiré d'un Proof Of Concept que je viens d'implémenter pour le plaisir avant JavaOne demain dans une heure "parce que je m'ennuyais" ;-) Le code est disponible ici: https://github.com/ktoso/jcurry
L'idée générale pourrait être étendue à FunctionN => FunctionM, relativement facilement, bien que la "sécurité de type réel" reste un problème pour l'exemple d'application partia et que l'exemple de curry aurait besoin de beaucoup de code standard dans jcurry , mais c'est faisable.
Dans l'ensemble, c'est faisable, mais dans Scala, c'est hors de la boîte ;-)
On peut émuler le curry avec 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
}
}
Oui, voyez l'exemple de code par vous-même:
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
}
}
Ceci est un exemple simple avec curriedAdd étant une fonction curry qui renvoie une autre fonction, et cela peut être utilisé pour une application partielle des paramètres tels que stockés dans curried qui est une fonction en soi. Ceci est maintenant appliqué plus tard complètement lorsque nous l'imprimons à l'écran.
De plus, plus tard, vous pouvez voir comment vous pouvez l'utiliser dans le style JS comme
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
Encore une fois sur les possibilités de Java 8:
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
Function<Integer, Integer> increment = y -> add.apply(1, y);
assert increment.apply(5) == 6;
Vous pouvez également définir des méthodes utilitaires comme celle-ci:
static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
return a2 -> f.apply(a1, a2);
}
Ce qui vous donne une syntaxe sans doute plus lisible:
Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
Currying une méthode est toujours possible en Java, mais il ne la prend pas en charge de manière standard. Essayer d'y parvenir est compliqué et rend le code assez illisible. Java n'est pas le langage approprié pour cela.
Un autre choix est ici pour 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);
}
alors vous pourriez réaliser le curry de cette façon
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();
Bien que vous puissiez faire du curry en Java, c'est moche (car ce n'est pas pris en charge). En Java, il est plus simple et plus rapide d'utiliser des boucles simples et des expressions simples. Si vous publiez un exemple d'utilisation du curry, nous pouvons vous suggérer des alternatives qui font la même chose.
2 * ?
En Java, vous le feriez avec une boucle.
Il s'agit d'une bibliothèque de currying et d'application partielle en Java:
https://github.com/Ahmed-Adel-Ismail/J-Curry
Il prend également en charge la déstructuration des tuples et Map.Entry dans les paramètres de méthode, comme par exemple en passant un Map.Entry à une méthode qui prend 2 paramètres, donc Entry.getKey () ira au premier paramètre, et Entry.getValue () ira pour le deuxième paramètre
Plus de détails dans le fichier README
L'avantage d'utiliser Currying dans Java 8 est qu'il vous permet de définir des fonctions d'ordre supérieur, puis de transmettre une fonction de premier ordre et des arguments de fonction d'une manière enchaînée et élégante.
Voici un exemple pour Calculus, la fonction dérivée.
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
}
}
Oui, je suis d'accord avec @ Jérôme, le traitement en Java 8 n'est pas supporté de manière standard comme dans Scala ou d'autres langages de programmation fonctionnelle.
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");
}
}