[10분 테코톡] 커링

흑우·2023년 12월 1일

10분 테코톡 - 5기

목록 보기
12/16

커링이란?

  • 커링이란 N개의 파라미터를 받는 함수를 N개의 단항함수(하나의 파라미터만 받는 함수)로 나누는 것
func(a,b,c) => func(a)(b)(c)

add(1,2,3) // 6
curriedAdd(1)(2)(3) // 6
  • 기존 함수의 파라미터를 하나씩 체인 형태로 호출하도록 만든다.
// 파라미터 a, b를 받아 더해서 리턴하는 함수 add
const add = (a, b) => a + b;

// 커링을 적용하면
// 첫 번째 함수가 a를 받고, 내부 함수가 b를 받는 형태로 된다.
// 최종적으로 모든 파라미터가 들어오면 add함수를 호출하는 형태
const curriedAdd = (a) => (b) => add(a + b);
const addWithOne = curriedAdd(1);

addWithOne(1); // 2
addWithOne(2); // 3
  • 커링 함수 curridAdd의 첫 번째 인수로 1을 넣어 호출하여 반환된 함수 addWithOne은 클로저가 된다. => 따라서 addWithOne은 받은 파라미터 1을 기억하고 있다.
  • 즉, 특정 상태가 고정된 함수가 만들어진다.
  • 마지막 파라미터가 전달되기 전까지는 원본 함수가 실행되지 않는다.
  • 즉, addWithOne은 curriedAdd(1)인 상태로 b를 대기하고 있는 함수이다.

커링을 적용하는 함수

const curry = (f) => (a) => (b) => f(a, b);

const add = (a, b) => a + b;
add(1, 2); // 3

const curriedAdd = curry(add);
curriedAdd(3)(3); // 6

적용 예시

['1', '10', '101', '1111'].map(parseInt);
  • 실행 결과가 어떻게 될까요? => [1, NaN, 5, 40]
  • 이렇게 되는 이유는 map은 callback의 파라미터로 첫 번째는 currentValue, 두 번째는 index값을 주고
  • parseInt는 첫 번째 인수로 string을 받고, 두 번째 인수로 radix(몇 진법으로 변환할 지)를 받기 때문입니다.
    • 2, 32를 벗어나면 NaN 반환
    • 따라서 아래와 같이 동작하고 있었던거죠!
parseInt('1', 0) // 1
parseInt('10', 1) // NaN
parseInt('101', 2) // 5
parseInt('1111', 3) // 40
  • radix를 명시하여 해결하기
['1', '10', '101', '1111'].map((str) => parseInt(str, 10));
  • 직접 명시하지 않고 처음 작성한 코드의 의도대로 작성할 방법은 없을까?

커링을 적용해보자

const r_curry = (f) => (b) => (a) => f(a, b);

const parseDecimal = r_curry(parseInt)(10);
  • r_curry는 함수 파라미터의 반대 순서대로 함수 f를 커링한다.
  • radix = 10으로 고정된 함수 parseDecimal이 만들어졌다.
  • 커링을 이용해 parseInt를 하나의 인자를 받도록 바꾸었다.
['1', '10', '101', '1111'].map(parseDecimal);
  • 적용해보면 의도한대로 동작한다.

정리

  • 커링은 N개의 파라미터를 받는 함수를 N개의 단항함수로 나누는 것
  • 커링된 함수는 코드의 가독성을 높인다
  • 커링은 함수를 재사용할 때 유용하다

마무리

이번 글에서는 커링에 대해서 다뤘는데요. 부끄럽게도 저는 처음 듣는 개념이었습니다. 예시를 들어서 설명을 해주셔서 이해가 잘됐습니다. 하지만 사용 예시 같은 부분은 추가적으로 찾아봐야할 거 같습니다. 해당 예시로 이해는 했으나 아직까지는 "굳이..?"라는 생각이 들어서 아직 제가 많이 부족한가봅니다. 하하하

Reference

profile
흑우 모르는 흑우 없제~

0개의 댓글