Java는 Currying을 지원합니까?
자바에서 그것을 끌어낼 방법이 없을까 생각하고 있습니다.폐쇄에 대한 원어민 지원 없이는 불가능하다고 생각합니다.
Java 8(2014년 3월 18일 출시)은 카레링을 지원합니다.missingfaktor에 의해 답변에 게시된 Java 코드 예는 다음과 같이 고쳐 쓸 수 있습니다.
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와 같은 대체 JVM 언어를 사용할 이유가 거의 없습니다.물론 다른 언어 기능도 제공하고 있지만, 이행 비용과 IDE/툴링/라이브러리 지원의 약화를 정당화하기에는 충분하지 않습니다.
자바에서는 반드시 커링과 부분 적용이 가능하지만, 필요한 코드의 양으로 인해 흥미를 잃을 수 있습니다.
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는 위의 Java 코드와 동등한 Haskell입니다.
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을 사용한 Currying에는 많은 옵션이 있습니다.Javaslang과 jOO는 모두 Currying을 바로 사용할 수 있습니다(JDK에서는 이것이 실수라고 생각됩니다).Cyclops Functions 모듈에는 Currying 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);
편집: 2014년 및 Java 8을 기점으로 Java의 기능 프로그래밍은 가능해질 뿐만 아니라 추악하지도 않습니다(아름답다고 감히 말할 수 있습니다).예를 들어 Rogerio의 답변을 참조하십시오.
오래된 답변:
기능적인 프로그래밍 기술을 사용한다면 Java는 최선의 선택이 아닙니다.missingfaktor가 썼듯이, 당신이 원하는 것을 이루기 위해서는 꽤 많은 양의 코드를 작성해야 할 것이다.
한편, JVM의 Java에 제한되지 않고 기능 언어인 Scala 또는 Clojure를 사용할 수 있습니다(Scala는 기능 언어 및 OO 언어입니다).
커링은 함수를 반환해야 합니다.이것은 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;
}
}
및 divide기능:
// 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
Java 7 Method Handles: 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 함수인 단순한 예이며, 이는 함수 자체인 curried에 저장된 파라미터의 부분적 적용에 사용할 수 있습니다.이것은 나중에 화면에 인쇄할 때 완전히 적용됩니다.
또한 나중에 JS스타일로 어떻게 사용할 수 있는지 확인하실 수 있습니다.
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
음, 스칼라, 클로주어, 하스켈어(또는 다른 기능적 프로그래밍 언어)는 카레링이나 다른 기능적 트릭에 사용하는 언어입니다.
수 있는 양의 할 수에 대해 만 봐주세요.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);
}
이것은, JavaOne 전에 재미삼아 실장하고 있던 개념 실증으로부터 얻은 것입니다.내일 1시간 후에 "심심해서";-) 코드는, https://github.com/ktoso/jcurry 에서 입수할 수 있습니다.https://github.com/ktoso/jcurry
일반적인 아이디어는 Function N = > Function M으로 비교적 쉽게 확장될 수 있지만, partia 어플리케이션의 예에서는 "실제 타입의 안전성"이 여전히 문제로 남아 있고, 커링 예에서는 jcury에 많은 보일러 플래티 코드가 필요하지만 실행할 수 있습니다.
대체로 실행할 수 있지만 Scala에서는 즉시 사용할 수 있습니다.
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에서는 Currying을 할 수 있지만 (지원되지 않기 때문에) Java에서는 플레인 루프와 간단한 표현을 사용하는 것이 간단하고 빠릅니다.카레를 사용하는 장소의 예를 게재해 주신다면, 같은 방법을 제안할 수 있습니다.
이것은 Java에서의 커링 및 부분 어플리케이션용 라이브러리입니다.
https://github.com/Ahmed-Adel-Ismail/J-Curry
또한 Tuples 및 Map 파괴도 지원합니다.메서드 파라미터 입력(예: Map 전달).2개의 파라미터를 갖는 메서드의 엔트리이므로 Entry.getKey()는 첫 번째 파라미터로 이동하고 Entry.getValue()는 두 번째 파라미터로 이동합니다.
README 파일의 상세
Java 8에서 Currying을 사용하면 상위 함수를 정의하고 1차 함수와 함수 인수를 체인으로 우아하게 전달할 수 있다는 장점이 있습니다.
다음은 미분함수인 미적분의 예입니다.
- 미분 함수 근사치를 (f(x+h)-f(x)/h로 정의합니다.이것이 상위 함수가 됩니다.
- 2개의 서로 다른 함수 1/x의 도함수와 표준화된 가우스 분포를 계산해 봅시다.
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érome에 동의합니다.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);
}
// Usage of `BiFnCurry.curry` defined below
someStream.map(curry(this::someBiFunction).apply(1stBiFnParam))
// someBiFunction is Function<1stBiFnParam, 2ndBiFnParam, R>
// curry(this::someBiFunction).apply(1stBiFnParam) returns Function<2ndBiFnParam, R>
public class BiFnCurry {
public static <T1, T2, R> Function<T1, Function<T2, R>> curry(BiFunction<T1, T2, R> biFn) {
return t1 -> (t2 -> biFn.apply(t1, t2));
}
}
언급URL : https://stackoverflow.com/questions/6134278/does-java-support-currying
'itsource' 카테고리의 다른 글
재귀적 콜라츠 추측 함수 프로그램이 출력을 제공하지 않습니다. (0) | 2022.10.26 |
---|---|
Python 로케일 오류: 지원되지 않는 로케일 설정 (0) | 2022.10.26 |
python과 return matches의 두 목록을 비교하려면 어떻게 해야 합니까? (0) | 2022.10.26 |
키별로 JavaScript 개체 정렬 (0) | 2022.10.26 |
foreach 루프의 어레이 값 수정 (0) | 2022.10.25 |