함수형 프로그래밍과 JavaScript ES6+ 강의를 듣고 정리한 내용입니다.
✏️ 예제
const fruits = [
{ name: '사과', price: 1000, quantity: 1 },
{ name: '바나나', price: 2000, quantity: 2 },
{ name: '딸기', price: 1500, quantity: 3 },
{ name: '레몬', price: 1000, quantity: 4 },
{ name: '복숭아', price: 3000, quantity: 5 }
];
const go = (...args) => reduce((a, f) => f(a), args);
const pipe =
(f, ...fs) =>
(...as) =>
go(f(...as), ...fs);
const curry =
(f) =>
(a, ..._) =>
_.length ? f(a, ..._) : (..._) => f(a, ..._);
const add = (a, b) => a + b;
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;
});
총 수량 구하기
go(
fruits,
map((f) => f.quantity),
reduce(add),
console.log
);
먼저 go 함수를 이용해 과일들의 총 수량을 구하는 방식은 위와 같다.
go 함수의 첫번째 인자로 iter인 fruits를 넣어주고 이후 값을 처리할 함수들을 인자로 넣어주는 것이다.
과일들의 총 수량을 구하는 것이 원하는 작업이기 때문에 map()으로 과일들의 수를 뽑아내어 reduce로 그 수를 더했다.
const total_nums = fruits => go(
fruits,
map((f) => f.quantity),
reduce(add)
);
console.log(total_nums(fruits));
이를 좀 더 활용성있게 쓰기 위해 total_nums라는 함수를 만들기로 했다.
total_nums()에서는 위의 코드와 같은 작업을 하지만 fruits 인자를 받아오고 그것을 go 함수에 넘겨주도록 했다.
이 작업을 거치면 다른 iter가 들어와 수량을 구한다고 했을 때 바뀐 iter를 인자로 주면 되기 때문에 수정이 용이하다.
const total_nums2 = pipe(
map((f) => f.quantity),
reduce(add)
);
console.log(total_nums2(fruits));
total_nums라는 함수를 만들고 보니 위의 go 함수 구문과 total_nums 함수 모두 평가되는 부분은 map과 reduce 함수 부분이다.
따라서 이 작업들을 pipe로 묶어 total_nums2라는 새 함수를 만들었다.
이 세 구문 모두 출력값은 15로 동일한 값을 가진다.
총 가격 구하기
const total_price = pipe(
map(f => f.price * f.quantity),
reduce(add)
);
console.log(total_price(fruits));
가격을 구하는 작업은 앞서 했던 총 수량 가격 구하는 방법과 비슷하다.
다만 add를 하기 전 add를 할 값들을 가져오는 map 함수에서 각 과일들의 가격과 양을 곱한 값을 가져오면 된다.
추상화 레벨 높이기 (1)
const sum = (f, iter) => go(
iter,
map(f),
reduce(add)
);
console.log(sum(f => f.quantity, fruits));
console.log(sum(f => f.price * f.quantity, fruits));
위의 과일들의 총 수량을 구하는 것과 총 가격을 구하는 것을 살펴보면 fruits에 대해 map()로 필요한 값을 가져온 후 reduce(add)를 한다.
따라서 좀 더 추상화시키기 위해 sum이라는 함수를 만들어 필요한 값을 만드는 함수와 iter를 인자로 받고 go 함수를 이용해 iter를 map(f)로 넘겨주기로 했다.
const total_price2 = (fruits) =>
sum((f) => f.price * f.quantity, fruits);
console.log(total_price2(fruits));
과일들의 전체 가격을 구하는 total_price()를 sum을 이용해 total_price2()를 구현했다.
이렇게 만들어도 total_price()와 하는 일은 동일하기 때문에 출력 결과도 동일하다.
추상화 레벨 높이기 (2) - curry
const sum2 = curry((f, iter) => go(
iter,
map(f),
reduce(add)
));
sum()을 curry로 묶은 sum2()를 만들었다.
이 과정을 통해 아까의 total_price2()를 더 짧게 만들 수 있다.
const total_price3 = sum2(f => f.price * f.quantity);
console.log(total_price3(fruits));
sum2()를 이용해 total_price3()를 만들었다.
total_price2()에 비해 더 짧은 total_price3()를 만들 수 있었던 이유는 다음과 같다.
total_price3가 선언될 때 sum2의 인자값으로 f => f.price * f.quantity 함수를 넣었다.
하지만 이때 sum2는 curry로 묶여있고 curry가 필요한 인자를 2개 이상 받지 못했기 때문에 인자로 넘어온 함수를 저장해둔다.
이후 console.log() 구문에서 total_price3에 fruits라는 인자를 하나 더 넘겨주었다.
이때 curry에서 필요한 인자 수를 충족했기 때문에 함수가 실행되고 결과값이 출력된다.
'JavaScript' 카테고리의 다른 글
[FP&ES6+] take (0) | 2021.12.25 |
---|---|
[FP&ES6+] range & L.range (0) | 2021.12.22 |
[FP&ES6+] go, pipe, curry - (2) (0) | 2021.12.10 |
[FP&ES6+] go, pipe, curry - (1) (0) | 2021.12.10 |
[FP&ES6+] map, filter, reduce (0) | 2021.12.01 |
댓글