[java] Java 8 : java.util.function에서 TriFunction (및 kin)은 어디에 있습니까? 아니면 대안은 무엇입니까?

java.util.function.BiFunction이 표시되므로 다음과 같이 할 수 있습니다.

BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };

충분하지 않고 TriFunction이 필요하면 어떻게합니까? 존재하지 않습니다!

TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };

나는 내 자신의 TriFunction을 정의 할 수 있다는 것을 알고 추가해야한다고 생각한다. 나는 단지 표준 라이브러리에 그것을 포함하지 않는 이유를 이해하려고 노력하고있다.



답변

내가 아는 한 파괴적인 기능과 건설적인 기능은 두 종류뿐입니다.

건설적인 기능은 이름에서 알 수 있듯이 무언가를 생성하지만 파괴적인 기능은 무언가를 파괴하지만 지금 생각하는 방식은 아닙니다.

예를 들어, 함수

Function<Integer,Integer> f = (x,y) -> x + y  

A는 건설적인 일. 당신이 무언가를 만들어야 할 때. 예제에서 튜플 (x, y)를 구성했습니다 . 구성 함수에는 무한 인수를 처리 할 수 ​​없다는 문제가 있습니다. 그러나 최악의 것은 논쟁을 열어 둘 수 없다는 것입니다. “글쎄, let x : = 1″이라고 말하고 시도하고 싶은 모든 y를 시도 할 수는 없습니다. 당신은 매번 전체 튜플을
x := 1. 그래서 당신을위한 기능이 돌아가 무엇을보고 싶어하면 y := 1, y := 2, y := 3당신이 작성해야합니다 f(1,1) , f(1,2) , f(1,3).

Java 8에서는 구성 적 람다 함수를 사용하는 이점이 많지 않기 때문에 구성 적 함수는 메서드 참조를 사용하여 처리해야합니다 (대부분의 경우). 그것들은 정적 메소드와 약간 비슷합니다. 사용할 수는 있지만 실제 상태는 없습니다.

다른 유형은 파괴적인 유형으로, 무언가를 가져와 필요한만큼 해체합니다. 예를 들어, 파괴 기능

Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y) 

f건설적인 기능과 동일합니다 . 파괴 함수의 장점은 이제 무한 인수를 처리 할 수 ​​있다는 것입니다. 이는 특히 스트림에 편리하며 인수를 열어 둘 수 있습니다. 다시 결과가하면 어떨까보고 싶다면 x := 1그리고 y := 1 , y := 2 , y := 3, 당신은 말할 수 h = g(1)
h(1)의 결과 y := 1, h(2)위해 y := 2h(3)위해 y := 3.

여기에 고정 된 상태가 있습니다! 그것은 매우 역동적이며 우리가 람다에서 원하는 대부분의 시간입니다.

Factory와 같은 패턴은 당신을 위해 일하는 함수를 넣을 수 있다면 훨씬 쉽습니다.

파괴적인 것은 서로 쉽게 결합됩니다. 유형이 맞으면 원하는대로 구성 할 수 있습니다. 이를 사용하면 (불변 값으로) 테스트를 훨씬 쉽게 만드는 형태를 쉽게 정의 할 수 있습니다!

건설적인 구성으로도 그렇게 할 수 있지만 파괴적인 구성은 더 멋지고 목록이나 장식 자처럼 보이며 건설적인 구성은 나무처럼 보입니다. 그리고 건설적인 기능을 사용한 역 추적과 같은 것은 좋지 않습니다. 파괴적인 기능 (동적 프로그래밍)의 부분 기능을 저장하고 “역 추적”에서 이전 파괴 기능을 사용하면됩니다. 따라서 코드가 훨씬 작아지고 가독성이 향상됩니다. 건설적인 함수를 사용하면 모든 인수를 기억할 정도가 많거나 적습니다.

그렇다면 왜 BiFunction없는 것보다 더 많은 의문이 있어야 할 필요 가 TriFunction있는가?

우선, 많은 시간 동안 몇 개의 값 (3 미만) 만 있고 결과 만 필요하므로 정상적인 파괴 함수는 전혀 필요하지 않으며 건설적인 함수는 괜찮습니다. 그리고 정말로 건설적인 기능이 필요한 모나드와 같은 것들이 있습니다. 그러나 그 외에는 전혀 존재하는 이유가 많지 않습니다 BiFunction. 그렇다고 제거해야한다는 의미는 아닙니다! 죽을 때까지 모나드를 위해 싸워요!

따라서 논리 컨테이너 클래스로 결합 할 수없는 인수가 많고 함수가 구성 적이어야하는 경우 메서드 참조를 사용합니다. 그렇지 않으면 파괴적인 기능의 새로운 기능을 사용하려고 시도하면 훨씬 적은 코드 라인으로 많은 일을 할 수 있습니다.


답변

TriFunction이 필요한 경우 다음을 수행하십시오.

@FunctionalInterface
interface TriFunction<A,B,C,R> {

    R apply(A a, B b, C c);

    default <V> TriFunction<A, B, C, V> andThen(
                                Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (A a, B b, C c) -> after.apply(apply(a, b, c));
    }
}

다음 작은 프로그램은 어떻게 사용되는지 보여줍니다. 결과 유형은 마지막 제네릭 유형 매개 변수로 지정됩니다.

  public class Main {

    public static void main(String[] args) {
        BiFunction<Integer, Long, String> bi = (x,y) -> ""+x+","+y;
        TriFunction<Boolean, Integer, Long, String> tri = (x,y,z) -> ""+x+","+y+","+z;


        System.out.println(bi.apply(1, 2L)); //1,2
        System.out.println(tri.apply(false, 1, 2L)); //false,1,2

        tri = tri.andThen(s -> "["+s+"]");
        System.out.println(tri.apply(true,2,3L)); //[true,2,3]
    }
  }

TriFunction에 대한 실용적인 사용이 있었 java.util.*거나 java.lang.*정의되었을 것입니다. 나는 22 개의 인수를 넘지 않을 것이다. 😉 내가 의미하는 바는, 컬렉션을 스트리밍 할 수있는 모든 새로운 코드가 메소드 매개 변수로 TriFunction을 필요로하지 않는다는 것이다. 그래서 그것은 포함되지 않았습니다.

최신 정보

완전성을 위해 다른 답변 (커링 관련)의 파괴 함수 설명을 따르기 위해 추가 인터페이스없이 TriFunction을 에뮬레이션하는 방법은 다음과 같습니다.

Function<Integer, Function<Integer, UnaryOperator<Integer>>> tri1 = a -> b -> c -> a + b + c;
System.out.println(tri1.apply(1).apply(2).apply(3)); //prints 6

물론 다른 방법으로 함수를 결합하는 것도 가능합니다. 예 :

BiFunction<Integer, Integer, UnaryOperator<Integer>> tri2 = (a, b) -> c -> a + b + c;
System.out.println(tri2.apply(1, 2).apply(3)); //prints 6
//partial function can be, of course, extracted this way
UnaryOperator partial = tri2.apply(1,2); //this is partial, eq to c -> 1 + 2 + c;
System.out.println(partial.apply(4)); //prints 7
System.out.println(partial.apply(5)); //prints 8

커링은 람다 이상의 기능적 프로그래밍을 지원하는 모든 언어에 자연 스럽지만 Java는 이러한 방식으로 빌드되지 않으며 달성 가능하지만 코드를 유지 관리하기 어렵고 때로는 읽기가 어렵습니다. 그러나 연습으로 매우 유용하며 때로는 부분 함수가 코드에서 적절한 위치에 있습니다.


답변

대안은 아래 종속성을 추가하는 것입니다.

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.0</version>
</dependency>

이제 최대 8 개의 인수와 같이 Vavr 함수를 사용할 수 있습니다.

3 가지 인수 :

Function3<Integer, Integer, Integer, Integer> f =
      (a, b, c) -> a + b + c;

5 가지 인수 :

Function5<Integer, Integer, Integer, Integer, Integer, Integer> f =
      (a, b, c, d, e) -> a + b + c + d + e;


답변

거의 같은 질문과 부분적인 답변이 있습니다. 건설적 / 해체 적 대답이 언어 디자이너가 염두에 둔 것인지 확실하지 않습니다. 3 개 이상이 N까지 유효한 사용 사례가 있다고 생각합니다.

나는 .NET에서 왔습니다. .NET에서는 void 함수에 대한 Func 및 Action이 있습니다. 술어 및 기타 특수 사례도 존재합니다. 참조 : https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx

언어 디자이너가 Function, Bifunction을 선택하고 DecaExiFunction까지 계속하지 않은 이유가 무엇인지 궁금합니다.

두 번째 부분에 대한 대답은 유형 삭제입니다. 컴파일 후 Func와 Func 사이에는 차이가 없습니다. 따라서 다음은 컴파일되지 않습니다.

package eu.hanskruse.trackhacks.joepie;

public class Functions{

    @FunctionalInterface
    public interface Func<T1,T2,T3,R>{
        public R apply(T1 t1,T2 t2,T3 t3);
    }

    @FunctionalInterface
    public interface Func<T1,T2,T3,T4,R>{
        public R apply(T1 t1,T2 t2,T3 t3, T4 t4);
    }
}

내부 기능은 또 다른 사소한 문제를 피하기 위해 사용되었습니다. Eclipse는 동일한 디렉토리의 Function이라는 파일에 두 클래스를 모두 갖도록 고집했습니다 … 요즘 컴파일러 문제인지 확실하지 않습니다. 그러나 Eclipse에서 오류를 돌릴 수 없습니다.

Func는 자바 함수 유형과 이름 충돌을 방지하기 위해 사용되었습니다.

따라서 3에서 16 개의 인수까지 Func를 추가하려면 두 가지를 수행 할 수 있습니다.

  • TriFunc, TesseraFunc, PendeFunc, … DecaExiFunc 등을 만드십시오.
    • (그리스어 또는 라틴어를 사용해야합니까?)
  • 패키지 이름이나 클래스를 사용하여 이름을 다르게 만드십시오.

두 번째 방법의 예 :

 package eu.hanskruse.trackhacks.joepie.functions.tri;

        @FunctionalInterface
        public interface Func<T1,T2,T3,R>{
            public R apply(T1 t1,T2 t2,T3 t3);
        }

package eu.trackhacks.joepie.functions.tessera;

    @FunctionalInterface
    public interface Func<T1,T2,T3,T4,R>{
        public R apply(T1 t1,T2 t2,T3 t3, T4 t4);
    }

최선의 접근 방법은 무엇입니까?

위의 예제에서는 andThen () 및 compose () 메서드에 대한 구현을 포함하지 않았습니다. 이것을 추가하는 경우 각각 16 개의 오버로드를 추가해야합니다. TriFunc에는 16 개의 인수가있는 andthen ()이 있어야합니다. 순환 종속성으로 인해 컴파일 오류가 발생합니다. 또한 Function 및 BiFunction에 대해 이러한 과부하가 발생하지 않습니다. 따라서 하나의 인수로 Func를 정의하고 두 개의 인수로 Func를 정의해야합니다. .NET에서 순환 종속성은 Java에없는 확장 메소드를 사용하여 회피됩니다.


답변

여기에서 BiFunction의 소스 코드를 찾았습니다.

https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/java/util/function/BiFunction.java

TriFunction을 만들기 위해 수정했습니다. BiFunction과 마찬가지로 compose ()가 아닌 andThen ()을 사용하므로 compose ()가 필요한 일부 응용 프로그램의 경우 적절하지 않을 수 있습니다. 정상적인 종류의 물체에는 괜찮습니다. andThen () 및 compose ()에 대한 좋은 기사는 여기에서 찾을 수 있습니다.

http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/

import java.util.Objects;
import java.util.function.Function;

/**
 * Represents a function that accepts two arguments and produces a result.
 * This is the three-arity specialization of {@link Function}.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object, Object)}.
 *
 * @param <S> the type of the first argument to the function
 * @param <T> the type of the second argument to the function
 * @param <U> the type of the third argument to the function
 * @param <R> the type of the result of the function
 *
 * @see Function
 * @since 1.8
 */
@FunctionalInterface
public interface TriFunction<S, T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param s the first function argument
     * @param t the second function argument
     * @param u the third function argument
     * @return the function result
     */
    R apply(S s, T t, U u);

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     */
    default <V> TriFunction<S, T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (S s, T t, U u) -> after.apply(apply(s, t, u));
    }
}


답변

3 개의 매개 변수를 사용하여 자신 만의 함수를 만들 수도 있습니다.

@FunctionalInterface
public interface MiddleInterface<F,T,V>{
    boolean isBetween(F from, T to, V middleValue);
}

MiddleInterface<Integer, Integer, Integer> middleInterface =
(x,y,z) -> x>=y && y<=z; // true


답변

항상 TriFunction에서 멈출 수는 없습니다. 때로는 n 개의 매개 변수를 함수에 전달해야 할 수도 있습니다. 그런 다음 지원 팀은 코드를 수정하기 위해 QuadFunction을 만들어야합니다. 장기적인 해결책은 추가 매개 변수로 Object를 생성 한 다음 기성품 Function 또는 BiFunction을 사용하는 것입니다.