Мне было интересно, есть ли способ вытащить это на Java. Я думаю, что это невозможно без встроенной поддержки замыканий.
90
Мне было интересно, есть ли способ вытащить это на Java. Я думаю, что это невозможно без встроенной поддержки замыканий.
Ответы:
Java 8 (выпущена 18 марта 2014 г.) поддерживает каррирование. Пример кода 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)); } }
... что очень приятно. Лично я не вижу причин использовать альтернативный язык JVM, такой как Scala или Clojure, с доступной Java 8. Они, конечно, предоставляют другие языковые функции, но этого недостаточно, чтобы оправдать стоимость перехода и более слабую поддержку IDE / инструментов / библиотек, IMO.
источник
(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
Каррирование и частичное применение в 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), а модуль Cyclops Functions имеет набор статических методов для каррирования функций 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; }
«Каррирование» также доступно для потребителей. Например, чтобы вернуть метод с 3 параметрами и 2 из уже примененных, мы делаем что-то похожее на это
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
Javadoc
источник
currying
вCurry.curryn
исходном коде.РЕДАКТИРОВАТЬ : Начиная с 2014 года и Java 8, функциональное программирование на Java теперь не только возможно, но и не уродливо (осмелюсь сказать, красиво). См., Например , ответ Роджерио .
Старый ответ:
Java - не лучший выбор, если вы собираетесь использовать методы функционального программирования. Как писал missingfaktor, вам придется написать довольно большой объем кода, чтобы добиться того, чего вы хотите.
С другой стороны, вы не ограничены Java на JVM - вы можете использовать Scala или Clojure, которые являются функциональными языками (Scala, по сути, является функциональным и объектно-ориентированным).
источник
Каррирование требует возврата функции . Это невозможно с java (без указателей на функции), но мы можем определить и вернуть тип, содержащий метод функции:
public interface Function<X,Z> { // intention: f(X) -> Z public Z f(X x); }
Теперь давайте сделаем простое деление. Нам понадобится разделитель :
// 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 (или любой другой функциональный язык программирования ...) определенно являются языками, которые можно использовать для каррирования и других функциональных приемов.
При этом, безусловно, можно каррировать с помощью Java без огромного количества шаблонов, которого можно было бы ожидать (ну, хотя необходимость явно указывать типы очень больно - просто взгляните на
curried
пример ;-)).Тесты сильфонные витрина как, выделки
Function3
в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); }
а также частичное применение , хотя в этом примере это не совсем безопасно:
@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); }
Это взято из Proof Of Concept, которое я только что реализовал ради развлечения перед JavaOne завтра через час, «потому что мне было скучно» ;-) Код доступен здесь: https://github.com/ktoso/jcurry
Общая идея может быть относительно легко расширена до FunctionN => FunctionM, хотя «настоящая безопасность типов» остается проблемой для примера приложения partia, а для примера каррирования потребуется чертовски много стандартного кода в 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 } }
источник
Да, посмотрите сами пример кода:
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 8:
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;
источник
Карринг метода всегда возможен в 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, это некрасиво (потому что не поддерживается). В Java проще и быстрее использовать простые циклы и простые выражения. Если вы опубликуете пример использования каррирования, мы можем предложить альтернативы, которые делают то же самое.
источник
2 * ?
В Java вы бы сделали это с помощью цикла.Это библиотека для каррирования и частичного применения на Java:
https://github.com/Ahmed-Adel-Ismail/J-Curry
Он также поддерживает деструктуризацию Tuples и Map.Entry в параметры метода, например, передачу Map.Entry методу, который принимает 2 параметра, поэтому Entry.getKey () перейдет к первому параметру, а Entry.getValue () пойдет по второму параметру
Подробнее в файле README
источник
Преимущество использования каррирования в Java 8 заключается в том, что он позволяет вам определять функции высокого порядка, а затем передавать функцию первого порядка и аргументы функции в цепочке элегантным образом.
Вот пример исчисления, производной функции.
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, curring в 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"); } }
источник
Хотя все остальные ответы сосредоточены на конкретных примерах, я все же хотел предоставить общее решение для превращения двоичных функций в каррированные.
private static <A, B, C> Function<A, Function<B, C>> Curry(BiFunction<A, B, C> f) { return a -> b -> f.apply(a, b); }
источник