본문 바로가기

CS/Java

Java FP 4. Currying

Currying

a technique of translating the evaluation of a function that takes multiple parameters into evaluating a sequence of functions, each with a single parameter.

그러니까 currying은 여러개의 인자를 받는 함수를 하나의 인자를 사용하는 함수 여러개로 바꾸는 것이다.

즉, doSomething(1, “Hello”, true) 대신에 doSomething(1)(“Hello”)(true)처럼 하는것!

 

이렇게 하면 함수를 재사용할 수도 있고, 여러 파라미터를 한 번에 다 받지 않고 몇 개만 받아서 함수의 실행을 늦출 수 있다. (사실 재사용 빼고 후자는 뭐가 장점인지 모르겠다)

정말 간단한 예제는 다음과 같다.

// curried function
IntFunction<IntFunction<IntFunction<Integer>>> fff = x -> y -> z -> x * y + z;

// fff returns a curried function y -> z -> 2 * y + z
IntFunction<IntFunction<Integer>> ff = fff.apply(2);

// ff returns a curried function z -> 2 * 3 + z
IntFunction<Integer> f = ff.apply(3);

// f returns 7
int result = f.apply(1);

 

 

이제 내가 틀렸던/어려웠던 문제들을 보자

 

Write three functions:

Multifunctional mapper (transformer) that accepts a list of operators (mappers) and returns a new operator. The returned operator accepts a list of integer numbers and sequentially applies each mapper to each number in the list (performs multiple transformations). The result is a list with transformed values.

 

In terms of the multifunctional mapper define an operator that multiplies by two each integer number and then add one to its. The operator is applied to each number in the input list.

In terms of the multifunctional mapper define an operator that squares each integer number and then calculates the next even number following it. The operator is also applied to each number in the input list.

 

To simplify the problem all function (represented by objects) are declared, you need to finish their realization.
Look carefully at definition of each function.

Also there is an example:
 identity operation that is defined in terms of the multifunctional mapper. It doesn't changes values in the input list. It repeats identity transformation three times just for example.


During testing all operators will be tested (including identity).

Example 1. An input list with integer numbers [1, 1, 1, 1].

  • identityTransformation returns the result list [1, 1, 1, 1].
  • multTwoAndThenAddOneTransformation returns the result list [3, 3, 3, 3].
  • squareAndThenGetNextEvenNumberTransformation returns the result list [2, 2, 2, 2].

Example 2. An input list with integer numbers [1, 2, 3].

  • identityTransformation returns the result list [1, 2, 3].
  • multTwoAndThenAddOneTransformation returns the result list [3, 5, 7].
  • squareAndThenGetNextEvenNumberTransformation returns the result list [2, 6, 10].
/**
 * The function accepts a list of mappers and returns an operator that accepts a list of integers
 * and sequentially applies each mapper to each value (perform a transformation)
 */
public static final Function<List<IntUnaryOperator>, UnaryOperator<List<Integer>>> multifunctionalMapper = 
    fList ->
    {
        IntUnaryOperator func = fList.stream().reduce(x->x, (acc, ne) ->ne.compose(acc));
        return arr -> arr.stream().map(func::applyAsInt).collect(Collectors.toList());
    };

/**
 * EXAMPLE: the operator transforms each number to the same number (perform the identity transformation)
 *
 * It returns a list of the same numbers.
 */
public static final UnaryOperator<List<Integer>> identityTransformation =
        multifunctionalMapper.apply(Arrays.asList(x -> x, x -> x, x -> x));

/**
 * The operator accepts an integer list. 
 * It multiplies by two each integer number and then add one to its.
 * 
 * The operator returns transformed integer list.
 */
public static final UnaryOperator<List<Integer>> multTwoAndThenAddOneTransformation =
    multifunctionalMapper.apply(Arrays.asList(x -> 2*x, x -> x+1 ));

/**
 * The operator accepts an integer list. 
 * It squares each integer number and then get the next even number following it.
 * 
 * The operator returns transformed integer list.
 */
public static final UnaryOperator<List<Integer>> squareAndThenGetNextEvenNumberTransformation =
    multifunctionalMapper.apply(Arrays.asList(x -> (x*x), x-> x%2==1? x+1:x+2));

 

이 문제는 multiFunctionalMapper를 짜는 게 핵심이다. multiFunctionalMapper를 좀 뜯어보자.

 

Function<List<IntUnaryOperator>, UnaryOperator<List<Integer>>> multifunctionalMapper

multiFunctioinalMapper는 함수들의 리스트를 인자로 받고, List<Integer> -> List<Integer> 인 UnaryOperator를 반환하는 함수이다. 이 때 input으로 들어오는 List<Integer>는 List<IntUnaryOperator>를 sequential하게 통과된 값으로 mapping되어야 한다. ( 나 이거 해석 못해서 계속 헤맸음...)

 

그니까 결국 fList 다 composition한 IntUnaryOperator하나 만들어서 List<Integer>.stream().map()에 인자로 넘겨주면 된다.

 

 

 

 

두번째 문제

 

Write three operators:

A reduce operator that accepts an initial value (seed) and a combiner function and then returns a new function that combines all values in the given integer range (inclusively) into one integer value (it's a simple form of reduction).

In terms of the reduce operator define a sum operator for summing integer values in the given range.

In terms of the reduce operator define a product operator for multiplying integer values in the given range.

Try not to use Stream API. Write the reducer yourself.

To simplify the problem all functions are declared, you need to finish their realization.
Look carefully at definition of each operator.

During testing all three operators will be tested. The left boundary <= the right boundary.

Example 1. Left boundary = 1, right boundary = 4.

  • sumOperator returns the result 10.
  • productOperator returns the result 24.

Example 2. Left boundary = 5, right boundary = 6.

  • sumOperator returns the result 11.
  • productOperator returns the result 30.
/**
 * The operator combines all values in the given range into one value
 * using combiner and initial value (seed)
 */
public static final BiFunction<Integer, IntBinaryOperator, IntBinaryOperator> reduceIntOperator = 
    (a, f) -> ((x, y)-> IntStream.rangeClosed(x, y).reduce(a, f));

/**
 * The operator calculates the sum in the given range (inclusively)
 */
public static final IntBinaryOperator sumOperator = 
	(a, b) -> IntStream.rangeClosed(a, b).reduce(0, (x, y) -> x+y);// write your code here

/**
 * The operator calculates the product in the given range (inclusively)
 */
public static final IntBinaryOperator productOperator = 
	(a, b) -> IntStream.rangeClosed(a, b).reduce(1, (x, y) -> x*y); // write your code here

이것도 솔직히 문제 이해 다 못하고 풀기 시작했다...

sumOperator랑 productOperator는 별로 어렵지 않고 reduceIntOperator가 중요하다.

 BiFunction<Integer, IntBinaryOperator, IntBinaryOperator> reduceIntOperator

reduceIntOperator는 integer와 함수를 받아서 함수를 리턴하는 함수이다. input Integer는 seed로 하고, 어떠한 (x, y)에 대해 그 범위내에 있는 수들에 function을 적용하는 simple reduction form을 만들면 된다.

별로 어려운 건 아닌데, 어떠한 (x, y)는 어디서 오는 거지,,,? 하며 시간을 날렸다. 그냥 함수의 인자인데 멍청이!!!

 

 

 

출처 : https://stepik.org/course/1595

 

Java. Functional programming

The course introduces elements of functional programming in Java 8. After completing this course, you should have a basic understanding of lambda expressions, functional interfaces, stream API, lazy evaluation, currying and monads.

stepik.org

아 설명을 잘 못해서 오답노트해도 하는 것 같지가 않다 ㅠㅜㅜ

티스토리는 에디터가 진짜 별로다!!!!!!!!!!!!!!!!!

포스트가 이상해 글씨크기가 난장판이야!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 

반성할 점

- 문제를 제대로 끝까지 읽고 이해하자!

 

 

'CS > Java' 카테고리의 다른 글

Java FP 5. Monads  (7) 2020.04.07
Java FP 3. Streams  (4) 2020.03.30
Java FP 2. Functions are objects  (13) 2020.03.26
Java FP 1. Lambda expressions and method references  (14) 2020.03.26