커링(Currying)과 함수합성(Function composition)

sean k·2023년 3월 30일
0

개념정리

목록 보기
6/7

1. 커링(Currying)

커링(Currying)은 함수형 프로그래밍에서 매우 중요한 개념 중 하나입니다. 커링은 함수를 더 작은 함수로 분리하는 기술로, 이를 통해 함수를 재사용 가능한 모듈로 만들 수 있습니다.
간단히 설명하면, 하나 이상의 인자를 받는 함수를 인자 하나만 받는 함수로 변환하는 것입니다. 이를 통해 인자 중 일부를 미리 설정하고, 나중에 나머지 인자를 전달하여 함수를 호출할 수 있습니다.

예를 들어, 두 수를 더하는 함수 add가 있다고 가정해보겠습니다.

function add(x, y) {
  return x + y;
}

이 함수를 커링을 사용하여 변환하면 아래와 같이 작성할 수 있습니다.

function addCurry(x) {
  return function(y) {
    return x + y;
  };
}

addCurry 함수는 인자 x를 받아서 내부에서 익명 함수를 반환합니다. 이 익명 함수는 인자 y를 받아서 x + y 값을 반환합니다. addCurry 함수를 사용하여 다음과 같이 add 함수를 만들 수 있습니다.

const add = addCurry(2);
console.log(add(3)); // 5

이제 add 함수는 addCurry 함수로부터 인자 2를 받아서 내부적으로 인자 y와 더해서 값을 반환하는 함수가 되었습니다. add(3)을 호출하면 2 + 3인 5가 출력됩니다.

1-1. 함수 인자의 일부를 미리 설정할 때

커링을 사용하면 함수의 일부 인자를 미리 설정하여 다른 인자를 전달할 때마다 함수를 호출할 필요 없이 미리 설정한 인자를 사용할 수 있습니다. 이는 함수를 재사용하고, 코드 중복을 방지하며, 가독성을 높일 수 있습니다.

function add(a, b) {
  return a + b;
}

const add5 = add.bind(null, 5); // 인자 a를 5로 미리 설정한 함수 생성

console.log(add5(3)); // 8
console.log(add5(7)); // 12

위 코드에서는 bind 메서드를 사용하여 add 함수의 첫 번째 인자 a를 5로 미리 설정하여 add5 함수를 생성합니다. 이후에 add5 함수를 호출할 때마다 a는 5로 고정되어 있습니다.

1-2. 연속적인 함수 호출 시 사용할 때

커링을 사용하면 함수를 연속적으로 호출할 때 코드의 가독성을 높일 수 있습니다. 이는 함수 호출이 중첩되어 복잡한 코드를 작성할 때 특히 유용합니다.

function add(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

console.log(add(1)(2)(3)); // 6
console.log(add(4)(5)(6)); // 15

위 코드에서는 add 함수를 정의할 때 내부 함수를 반환하도록 구현합니다. 이후에 함수를 호출할 때는 함수 호출 결과를 다시 함수로 받아서 연속적으로 호출합니다.

1-3. 복잡한 함수의 인자를 분리하여 사용할 때

커링을 사용하면 함수의 인자를 분리하여 사용할 수 있습니다.

function calculateTotal(price, tax, discount) {
  return price * (1 + tax / 100) - discount;
}

const applyTax = function(tax) {
  return function(price) {
    return function(discount) {
      return calculateTotal(price, tax, discount);
    };
  };
};

const apply10PercentTax = applyTax(10);

console.log(apply10PercentTax(100)(5)); // 105
console.log(apply10PercentTax(50)(2)); // 53

위 코드에서는 calculateTotal 함수에서 계산에 필요한 인자를 받아 계산합니다. applyTax 함수를 정의하여 인자 tax를 받은 후 내부 함수를 반환합니다. 내부 함수는 인자 price를 받은 후 또 다른 내부 함수를 반환합니다. 이 내부 함수는 인자 discount를 받은 후 calculateTotal 함수를 호출하여 계산 결과를 반환합니다. 이러한 방식으로 applyTax 함수를 사용하면 인자 tax를 미리 설정하고, 나중에 price와 discount만 전달하여 calculateTotal 함수를 호출할 수 있습니다.

2. 함수 합성(Function Composition)

함수 합성은 두 개 이상의 함수를 결합하여 새로운 함수를 만드는 것을 말합니다.

function addOne(x) {
  return x + 1;
}

function double(x) {
  return x * 2;
}

이제 이 두 함수를 합성하여 새로운 함수를 만들어보겠습니다.

const addOneAndDouble = function(x) {
  return double(addOne(x));
};

console.log(addOneAndDouble(3)); // 8

위 코드에서 addOne 함수와 double 함수를 합성하여 addOneAndDouble 함수를 정의했습니다. addOneAndDouble 함수는 인자 x를 받아 addOne 함수를 먼저 호출한 후, 그 결과를 double 함수에 전달하여 반환합니다. 함수 합성을 사용하면 각 함수가 하나의 작은 문제를 해결하도록 분리하여 코드를 작성할 수 있습니다.

함수 합성을 수행하는 방법에는 여러 가지가 있습니다. 위의 예제에서는 함수 호출을 이용하여 함수를 합성했지만, 함수 합성을 위해 다른 방법들도 존재합니다. compose 함수는 여러 개의 함수를 인자로 받아서, 이를 합성하여 새로운 함수를 반환합니다. 이렇게 반환된 새로운 함수는 처음부터 순서대로 인자를 받아서 실행됩니다.

예를 들어, 다음과 같이 compose 함수를 사용하여 커링된 함수들을 합성하여 새로운 함수를 생성할 수 있습니다.

const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

const toUpperCase = str => str.toUpperCase();
const addExclamation = str => str + '!';
const repeatThreeTimes = str => str.repeat(3);

const stringManipulator = compose(
  repeatThreeTimes,
  addExclamation,
  toUpperCase
);

console.log(stringManipulator('hello')); // HELLO!HELLO!HELLO!

위의 코드에서 toUpperCase, addExclamation, repeatThreeTimes 함수는 각각 문자열을 인자로 받아 처리를 수행합니다. 이 함수들을 compose 함수를 이용하여 조합하여 stringManipulator 함수를 만들었습니다. 이제 stringManipulator 함수를 호출하면 toUpperCase, addExclamation, repeatThreeTimes 함수들이 오른쪽에서 왼쪽으로 차례로 실행되어 최종 결과값을 계산합니다. 따라서 위의 코드에서 stringManipulator('hello')은 'hello'를 대문자로 변환하여(toUpperCase 후) '!'를 추가하고(addExclamation 후) 해당 문자열을 세번 반복(repeatThreeTimes 후)한 결과인 'HELLO!HELLO!HELLO!'를 리턴합니다.

pipe 함수는 compose 함수와 유사하지만 함수를 왼쪽에서 오른쪽으로 조합합니다.

const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const add = x => y => x + y;
const multiply = x => y => x * y;

const addAndMultiply = pipe(
  add(2),
  multiply(3)
);

console.log(addAndMultiply(4)); // 18

위의 코드에서 add와 multiply 함수는 각각 하나의 인자를 받아 새로운 함수를 반환합니다. 이렇게 커링된 함수를 pipe 함수를 이용하여 조합하여 addAndMultiply 함수를 만들었습니다. 이제 addAndMultiply 함수를 호출하면 add 함수를 먼저 실행하고(add(2)), 그 결과값을 multiply 함수로 전달하여(multiply(3)), 최종 결과값을 계산합니다.

compose 함수와 pipe 함수의 차이점은 함수 조합 순서입니다. compose 함수는 오른쪽에서 왼쪽으로 함수를 조합하고, pipe 함수는 왼쪽에서 오른쪽으로 함수를 조합합니다. 이외에는 두 함수의 동작 방식이 거의 동일합니다.

0개의 댓글