curry라는 함수 역시 함수를 값으로 다루면서 어떤함수를 원하는 시점에 평가시키는 함수이다.
curry라는 함수는 함수를 받아서 함수를 리턴하고 인자를 받아서 인자가 원하는 갯수만큼의 인자가 들어왔을 때 받아두었던 함수를 나중에 평가시키는 함수이다.
첫번째 인자와 나머지 인자(a, ..._)를 받는데 만약에 인자가 두개이상 전달 되었을때라는 것을 뜻하는 것은 _에 length가 있을 때 일것이다.
length가 있다면 받아둔 함수를 즉시 실행하고( f(a, ..._) ) 만약아 아니라면 다시한번 함수를 리턴한다. 그리고 그 이후에 들어올 값들을 받아오고 함수를 실행하는 함수이다.
즉 미리받은 a와 새로받은 ..._인자를 전달하는 것이다.
정리하면 함수를 받아서 함수를 리턴하고 실행되었을 때 인자가 두개 이상이라면 받아둔 함수를 즉시 실행을 하고 인자가 두개보다 작다면 함수를 리턴한 후에 이후에 받은 인자들을 합쳐서 실행하는 함수이다.
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
아래와 같은 함수가 있다고 하자. 아래함수를 curry로 싸서 만들게 되면 함수를 전달해서 함수가 즉시 리턴이 된다.
mult를 확인해보면 아래와 같이 나타내진다.
<script>
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const mult = curry((a, b) => a * b);
log(mult);
</script>
그리고 mult함수에 인자를 하나만 전달해도 역시 함수이다. 나머지 인자를 전달했을 때(..._) 받아두었던 함수에게 받아두었던 인자와f(a) 지금 받은f(..._)인자를 전달하도록 되어있다.
console.log(mult(1));
실행을 하면서 결과를 전달하면 아래와 같이 결과가 나온다.
console.log(mult(1)(2));
그래서 어떤함수를 만들어 놓고 아래와 같은 패턴으로 사용할 수 있다.
const mult3 = mult(3);
console.log(mult3(10));
console.log(mult3(5));
console.log(mult3(3));
또 go에서 사용했던 코드를
<script>
go(
products,
products => filter(p => p.price < 20000, products),
products => map(p => p.price, products),
prices => reduce(add, prices),
log);
</script>
filter와 map과 reduce를 모드 curry를 적용해주면 즉 사용하고 있는 fx.js에서 map filter reduce에 각각 curry를 적용해주면 이 모든 함수들일 인자를 하나만 받으면 일단 이후인자들을 더 받기로 기다리는 함수를 리턴하도록 되어있다.
const log = console.log;
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const map = curry((f, iter) => {
let res = [];
for (const a of iter) {
res.push(f(a));
}
return res;
});
const filter = curry((f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
});
const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
});
즉 아래와 같이 동작이 될 것이다.
<script>
go(
products,
products => filter(p => p.price < 20000)(products),
products => map(p => p.price)(products),
prices => reduce(add)(prices),
log);
</script>
// 30000
즉 products를 받아서 products에 전달하는 것은(products) products받는 filter(p => p.price < 20000)의 결과가
그냥 filter(p => p.price < 20000)으로 해도 동작한다는 것이다. map(p => p.price)과 reduce(add)도 마찬가지로 아래와 같이 작성해도 동작한다.
<script>
go(
products,
filter(p => p.price < 20000),
map(p => p.price)),
reduce(add),
log);
</script>
// 30000
결론적으로 아래go코드를 더 쉬운 코드로 바꾼 것이다. 원래 맨처음 코드를 go함수를 통해서 순서를 반대로 뒤집고 currying을 통해서 간결한 표현을 만들었고 함수를 값으로 다루는 여러가지 함수들을 이용해서 깔끔한 코드를 만들었다.
log(
reduce(
add,
map(p => p.price,
filter(p => p.price < 20000, products))));
//
<script>
go(
products,
products => filter(p => p.price < 20000, products),
products => map(p => p.price, products),
prices => reduce(add, prices),
log);
</script>
//
<script>
go(
products,
filter(p => p.price < 20000),
map(p => p.price)),
reduce(add),
log);
</script>
// 30000
<출처 : 유인동 함수형 프로그래밍과 JavaScript ES6+>
https://www.inflearn.com/course/functional-es6/dashboard
함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의
ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,
www.inflearn.com
'JavaScript > 함수형 프로그래밍과 JavaScript ES6+' 카테고리의 다른 글
장바구니 예제(총 수량, 총 가격) (0) | 2021.08.19 |
---|---|
함수 조합으로 함수 만들기 (0) | 2021.08.19 |
go를 사용하여 읽기 좋은 코드로 만들기 (0) | 2021.08.19 |
코드를 값으로 다루어 표현력 높이기(pipe) (0) | 2021.08.19 |
코드를 값으로 다루어 표현력 높이기(go) (0) | 2021.08.19 |