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 := 2
및 h(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을 필요로하지 않는다는 것이다. 그래서 그것은 포함되지 않았습니다.
최신 정보
For completeness and following the destructive functions explanation in another answer (related to currying), here is how TriFunction can be emulated without additional interface:
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
Of course, it is possible to combine functions in other ways, e.g.:
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
While currying would be natural to any language that supports functional programming beyond lambdas, Java is not built this way and, while achievable, the code is hard to maintain, and sometimes read. However, it is very helpful as an exercise, and sometimes partial functions have a rightful place in your code.
I Have almost the same question and a partial answer. Not sure whether the constructive/deconstructive answer is what the language designers had in mind. I think having 3 and more upto N has valid use cases.
I come from .NET. and in .NET you have Func and Action for void functions. Predicate and some other special cases also exist. See: https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
I wonder what the reason was why the language designers opted for Function, Bifunction and did not continue until DecaExiFunction?
The answer to the second part is type erasure. After compilation there is no difference between Func and Func. The following therefore does not compile:
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);
}
}
Inner functions were used to circumvent another minor problem. Eclipse insisted on having both classes in files named Function in the same directory... Not sure whether this a compiler issue nowadays. But I cannot turn the error of in Eclipse.
Func was used to prevent name clashes with the java Function type.
So if you want to add Func from 3 upto 16 argument you can do two things.
- Make TriFunc, TesseraFunc,PendeFunc, ...DecaExiFunc etc
- (Should I use Greek or Latin?)
- Use package names or classes to make the names different.
Example for the second way:
package eu.hanskruse.trackhacks.joepie.functions.tri;
@FunctionalInterface
public interface Func<T1,T2,T3,R>{
public R apply(T1 t1,T2 t2,T3 t3);
}
and
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);
}
What would be the best approach?
In the above examples I did not include implementations for the andThen() and compose() methods. If you add these you must add 16 overloads each: the TriFunc should have an andthen() with 16 arguments. That would give you a compile error because of circular dependencies. Also you would not have these overloads for Function and BiFunction. Therefore you should also define Func with one argument and Func with two arguments. In .NET circular dependencies would be circumvented by using extension methods which are not present in Java.
Alternative is, add the below dependency,
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.0</version>
</dependency>
Now, you can use Vavr Function, like below upto 8 arguments,
3 arguments:
Function3<Integer, Integer, Integer, Integer> f =
(a, b, c) -> a + b + c;
5 arguments:
Function5<Integer, Integer, Integer, Integer, Integer, Integer> f =
(a, b, c, d, e) -> a + b + c + d + e;
I found the source code for BiFunction here:
I modified it to create TriFunction. Like BiFunction, it uses andThen() and not compose(), so for some applications that require compose(), it may not be appropriate. It should be fine for normal kinds of objects. A good article on andThen() and compose() can be found here:
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));
}
}
You could also create your own function taking the 3 parameters
@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
'development' 카테고리의 다른 글
S3에 데이터를 저장하고 Rails API / iOS 클라이언트를 사용하여 안전한 방식으로 사용자 액세스를 허용하는 방법은 무엇입니까? (0) | 2020.09.03 |
---|---|
P99 지연이란 무엇입니까? (0) | 2020.09.03 |
의사 다항식 시간이란 무엇입니까? (0) | 2020.09.02 |
함수가있는 JavaScript 삼항 연산자 예제 (0) | 2020.09.02 |
Mac OS X의 Vim 삽입 모드 (0) | 2020.09.02 |