함수형 시리즈
이전 클로저 포스팅에 이어 이번에는 커링이다.
커링?
사진 속 연산을 이해할 수 있는가?
이해할 수 있다면 이 포스팅은 스킵하면 된다.
커링(Currying)은 함수형 프로그래밍에서 등장하는 필수 개념이다.
쉽게 말하면, '함수를 반환하는 함수'이다.
커링이 왜 사용될까?
- 함수의 재활용을 위해서
- 원하는 함수들을 조합해서 사용할 수 있다.
- 하나 이상의 인수의 함수를, 하나의 인수를 받는 함수로 축소할 수 있다.
- 그래서 더 가벼운 함수 제작이 가능하다.
그럼 함수의 재활용이란 무엇일까?
function add(a,b){
return a+b;
}
다음 add 함수를 재활용 하려면?
function addTwo(a){
return add(a, 2);
}
이렇게 2 + @를 해주는 addTwo 함수를 만들 수 있다.
만약 add3, add4, add5 ... 등의 여러 함수가 필요하다고 하자.
function addTwo(a){
return add(a, 2);
}
function addThree(a){
return add(a, 3);
}
function addFour(a){
return add(a, 4);
}
분명 add 함수를 재활용하긴 하지만, 뭔가 이상하다.
여기에 커링을 적용해보자.
function addX(x){
return function(a){
return add(a, x);
}
}
const addTwo = addX(2);
const addThree = addX(3);
const addFour = addX(4);
addTwo(2) // 2+2 = 4
addThree(5) // 3+5 = 8
addFour(1) // 4+1 = 5
다음과 같이 addX 함수를 이용해서 여러 파생 함수들을 만들 수 있다.
addX 함수는 이렇게 동작한다.
- 인자 x를 받아서 익명함수를 반환한다. 그 함수는 function(a){return add(a,x);} 형태이다.
- 반환받은 값인 함수는 인자 a를 요구한다. a를 넣어주면 add(a,x)의 실행 결과를 반환한다.
이처럼 함수 실행을 위한 인자를 한번에 받지 않고, 여러 차례에 나눠서 받을 수 있다.
이 예시를 화살표 함수로 나타내면,
function addX(x){
return function(a){
return add(a, x);
}
}
const addX = x => a => add(a,x);
이렇게 더 간단해진다.
함수를 인자로 받는 커링함수 만들어보기.
자바스크립트에서는 함수도 값이다.
그러므로 함수의 인자로 또 다른 함수도 받을 수 있다.
function curry(fn){
return function(a){
return function(b){
return fn(a,b);
}
}
}
이제 이 함수를 어떻게 써야할지가 대충 보일 것인데,
정확히 위에서 선언했던 addX와 동일하게도 만들 수 있다.
function add(a,b){
return a+b;
}
const addX = curry(add);
const addTwo = addX(2);
console.log(addTwo(5)) // 5+2 = 7
함수 자체를 인자로 받아서 활용할 수 있는 것을 알게 되면, 코드를 작성하는 스타일이 더 다양해진다.
이제는 글 맨 앞에서 다뤘던 괴상한 화살표함수의 모습에 대해서 왜 동작하는지를 이해할 수 있다.
const curry = fn => fn2 => a => b => fn(a,b)+fn2(a,b)
// 이 함수를 뜯어보면 다음과 같다.
function curry(fn){
return function(fn2){
return function(a){
return function(b){
return fn(a,b) + fn2(a,b);
}
}
}
}
add, multiply 함수를 차례대로 생성해서 방정식을 만들어보자.
function add(a,b){
return a+b;
}
function multiply(a,b){
return a*b;
}
const fnIsAdd = curry(add);
const fnIsAddFn2IsMultiply = fnIsAdd(multiply);
const fnIsAddFn2IsMultiplyAis3 = fnIsAddFn2IsMultiply(3);
const add3and5Plusmultiply3and5 = fnIsAddFn2IsMultiplyAis3(5);
console.log(add3and5Plusmultiply3and5) // (3+5) + (3*5) = 8+15 = 23
const oneline = curry(add)(multiply)(3)(5);
console.log(oneline) // (3+5) + (3*5) = 8+15 = 23
처음에는 각각의 절차를 나타내봤고, 한번에 작성한 online 변수를 작성해봤다.
커링 응용하기
마지막으로 커링의 가벼운 응용을 보고 마칠 예정이다.
방금 인자로 함수도 받을 수 있다는 것을 알았다.
그렇다면 정말 '다양한 함수들을 조합'하는 길이 열린 셈이다.
이제 조금 더 간결하게 function 키워드 없이 화살표 함수로 커링함수를 표현하겠다.
방정식 4x * (x+2)을 수행할 함수를 만들어보자.
const add = (a,b) => a+b;
const multiply = (a,b) => a*b;
const addX = x => a => add(a, x);
const addTwo = addX(2);
const multiplyX = x => a => multiply(a, x);
const multiplyFour = multiplyX(4);
이렇게 커링을 이용해 addTwo, multiplyFour 함수를 만들었다.
const compose = fn => fn2 => x => fn2(x) * fn(x);
const equation = compose(addTwo)(multiplyFour);
equation(10) // (4 * 10) * (10 + 2) = 40 * 12 = 480
equation 함수에 넣는 값이 x가 되어 4x(x+2)의 다양한 값에 대한 결과를 받을 수 있다.
만약 추가적인 요구사항이 생겨서, 5x(x+2)의 결과를 반환하는 함수가 필요하다면?
const multiplyFive = multiplyX(5);
const equaion2 = compose(addTwo)(multiplyFive);
equation2(10) // (5 * 10) * (10 + 2) = 600
이렇게 표현할 수 있다.
마지막으로 위에서 선언한 equation 함수를 생성하면서 중복되는 부분을 제거하면,
const addTwo = addX(2);
const addFour = addX(4);
const multiplyFour = multiplyX(4);
const multiplyFive = multiplyX(5);
const compose = fn => fn2 => x => fn2(x) * fn(x);
const composeAddTwo = compose(addTwo);
const equation1 = composeAddTwo(multiplyFour); // 4x(x+2)
const equation2 = composeAddTwo(multiplyFive); // 5x(x+2)
const equation3 = composeAddTwo(addFour) // (x+4)(x+2)
위와 같이 인자로 받은 함수들을 교체해주면서, 유사한 함수들을 여러개 만들 수 있다.
아주 간단한 예시들을 통해 여러 함수들을 유용하게 조합할 수 있는 커링을 살펴보았다.
커링을 통한 함수 합성은 어려워지려면 한도 끝도 없이 어려워 질 수 있기 때문에...
함수형 프로그래밍을 프로젝트에 적용하면서 함수 조합 및 합성을 고려해보면 커링에 대해 진하게 느끼지 않을까 싶다.
(사실 빡세게 머리를 굴려도 간단한 재활용 함수들을 만들기조차 힘들더라..ㅎㅎ)
'JavaScript > theory' 카테고리의 다른 글
엄격모드. "use strict" (0) | 2022.07.20 |
---|---|
이벤트 위임, 버블링, 캡쳐링 (Event Delegation, bubbling, capturing) (0) | 2022.07.11 |
[자바스크립트] 마이크로 태스크 큐의 비동기 작업 처리와 렌더링 시점을 알아보자. (1) | 2022.04.14 |
브라우저/노드 환경에서 모듈, AMD, CommonJS, UMD 알아보기 (1) | 2022.02.15 |
자바스크립트의 this는 어떻게 결정되는가? (0) | 2022.01.25 |