Mi chiedevo se esiste un modo per estrarlo in Java. Penso che non sia possibile senza il supporto nativo per le chiusure.
Mi chiedevo se esiste un modo per estrarlo in Java. Penso che non sia possibile senza il supporto nativo per le chiusure.
Risposte:
Java 8 (rilasciato il 18 marzo 2014) supporta il curry. Il codice Java di esempio pubblicato nella risposta da missingfaktor può essere riscritto come:
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));
}
}
... che è abbastanza carino. Personalmente, con Java 8 disponibile, vedo poche ragioni per utilizzare un linguaggio JVM alternativo come Scala o Clojure. Forniscono altre funzionalità linguistiche, ovviamente, ma questo non è sufficiente per giustificare il costo della transizione e il più debole supporto per IDE / strumenti / librerie, IMO.
(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
Il currying e l'applicazione parziale sono assolutamente possibili in Java, ma la quantità di codice richiesta probabilmente ti spegnerà.
Alcuni codici per dimostrare il currying e l'applicazione parziale 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 qui è l'equivalente Haskell del codice Java sopra:
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
Ci sono molte opzioni per il currying con Java 8. Tipo di funzione Javaslang e jOOλ che offrono entrambi Currying out of the box (penso che questa fosse una svista nel JDK), e il modulo Cyclops Functions ha una serie di metodi statici per Currying JDK Functions e riferimenti al metodo. Per esempio
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;
}
Il "currying" è disponibile anche per i consumatori. Ad esempio, per restituire un metodo con 3 parametri e 2 di quelli già applicati, facciamo qualcosa di simile a questo
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
currying
nel Curry.curryn
codice sorgente.
EDIT : A partire dal 2014 e Java 8, la programmazione funzionale in Java ora non è solo possibile, ma anche non brutta (oso dire bella). Vedi ad esempio la risposta di Rogerio .
Vecchia risposta:
Java non è la scelta migliore, se intendi utilizzare tecniche di programmazione funzionale. Come ha scritto Missingfaktor, dovrai scrivere una grande quantità di codice per ottenere ciò che desideri.
D'altra parte, non sei limitato a Java su JVM: puoi usare Scala o Clojure che sono linguaggi funzionali (Scala è, infatti, sia funzionale che OO).
Il curry richiede la restituzione di una funzione . Questo non è possibile con java (nessun puntatore a funzione) ma possiamo definire e restituire un tipo che contiene un metodo di funzione:
public interface Function<X,Z> { // intention: f(X) -> Z
public Z f(X x);
}
Ora curiamo una semplice divisione. Abbiamo bisogno di un divisore :
// 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;
}
}
e una 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);
}
Ora possiamo fare una divisione al curry:
DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5
Bene, Scala , Clojure o Haskell (o qualsiasi altro linguaggio di programmazione funzionale ...) sono sicuramente I linguaggi da usare per il currying e altri trucchi funzionali.
Detto questo, è certamente possibile curry con Java senza la quantità eccessiva di boilerplate che ci si potrebbe aspettare (beh, dover essere espliciti sui tipi fa molto male però - basta dare un'occhiata curried
all'esempio ;-)).
I test muggiscono vetrina sia, currying un 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);
}
così come l' applicazione parziale , anche se in questo esempio non è veramente sicuro:
@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);
}
Questo è tratto da una prova di concetto che ho appena implementato per divertimento prima di JavaOne domani tra un'ora "perché ero annoiato" ;-) Il codice è disponibile qui: https://github.com/ktoso/jcurry
L'idea generale potrebbe essere espansa a FunctionN => FunctionM, in modo relativamente semplice, sebbene la "vera sicurezza dei tipi" rimanga un problema per l'esempio dell'applicazione partia e l'esempio currying richiederebbe un sacco di codice boilerplaty in jcurry , ma è fattibile.
Tutto sommato, è fattibile, ma in Scala è fuori dagli schemi ;-)
Si può emulare il currying con 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
}
}
Sì, guarda tu stesso l'esempio di codice:
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
}
}
Questo è un semplice esempio in cui curriedAdd è una funzione curry che restituisce un'altra funzione, e questa può essere utilizzata per l' applicazione parziale dei parametri come memorizzati in curry che è una funzione in sé. Questo viene ora applicato completamente quando lo stampiamo sullo schermo.
Inoltre, in seguito puoi vedere come puoi usarlo in una sorta di stile JS come
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
Un altro approccio alle possibilità di 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;
Puoi anche definire metodi di utilità come questo:
static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
return a2 -> f.apply(a1, a2);
}
Il che ti dà una sintassi probabilmente più leggibile:
Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
Il curry di un metodo è sempre possibile in Java, ma non lo supporta in modo standard. Cercare di ottenere ciò è complicato e rende il codice abbastanza illeggibile. Java non è il linguaggio appropriato per questo.
Un'altra scelta è qui per 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);
}
allora potresti ottenere il curry in questo modo
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();
Anche se puoi fare Currying in Java, è brutto (perché non è supportato) In Java è più semplice e veloce usare loop semplici ed espressioni semplici. Se pubblichi un esempio di dove useresti il curry, possiamo suggerirti alternative che fanno la stessa cosa.
2 * ?
In Java lo faresti con un ciclo.
Questa è una libreria per il currying e l'applicazione parziale in Java:
https://github.com/Ahmed-Adel-Ismail/J-Curry
Supporta anche la destrutturazione di tuple e Map.Entry nei parametri del metodo, come ad esempio il passaggio di Map.Entry a un metodo che accetta 2 parametri, quindi Entry.getKey () andrà al primo parametro e Entry.getValue () andrà per il secondo parametro
Maggiori dettagli nel file README
Il vantaggio dell'utilizzo di Currying in Java 8 è che consente di definire funzioni di ordine elevato e quindi passare una funzione del primo ordine e argomenti di funzione in modo concatenato ed elegante.
Ecco un esempio per Calculus, la funzione derivativa.
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
}
}
Sì, sono d'accordo con @ Jérôme, il curriculum in Java 8 non è supportato in modo standard come in Scala o altri linguaggi di programmazione funzionale.
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");
}
}