FP Workshop - 1. Functional vs Imperative
Table of contents
Chapter 1: Functional vs Imperative
명령형 스타일로 된 코드를 함수형 스타일로 바꾸어 보기
js
const products = [
{ id: "candy", name: "Candy", price: 10, onSale: true },
{ id: "ice-cream", name: "Ice cream", price: 20, onSale: true },
{ id: "cake", name: "Cake", price: 30, onSale: false },
{ id: "donuts", name: "Donuts", price: 12, onSale: true },
{ id: "chocolate", name: "Chocolate", price: 15, onSale: false },
{ id: "flower", name: "Flower", price: 40, onSale: false },
{ id: "sofa", name: "Sofa", price: 120, onSale: true },
{ id: "bed", name: "Bed", price: 400, onSale: true },
];
1. Imperative
가격이 30 이하인 제품 중에서 3개를 뽑고 그 가격들의 총합을 구하기.
js
// Imperative
function imperative() {
let total = 0;
let count = 0;
for (const a of products) {
if (a.price < 30) {
total += a.price;
count++;
}
if (count === 3) break;
}
console.log(total);
}
2. Functional
js
const isIterable = (iter) => typeof iter[Symbol.iterator] === "function";
const curry =
(f) =>
(a, ...args) =>
args.length ? f(a, ...args) : (...args2) => f(a, ...args2);
const map = curry(function* (f, iter) {
for (const a of iter) {
yield f(a);
}
});
const filter = curry(function* (f, iter) {
for (const a of iter) {
if (f(a)) yield a;
}
});
const take = curry((length, iter) => {
const res = [];
for (const a of iter) {
res.push(a);
if (res.length === length) return res;
}
});
const reduce = curry((f, acc, iter) => {
if (!iter && isIterable(acc)) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
});
const add = curry((a, b) => a + b);
const pipe =
(...args) =>
reduce((a, f) => f(a), args);
// Functional
pipe(
products,
map((a) => a.price),
filter((p) => p < 30),
take(3),
reduce(add),
console.log,
);
3. Generator 와 Lazy Evaluation(지연평가)
js
pipe(
products,
map((a) => a.price), // Lazy
filter((p) => p < 30), // Lazy
take(3),
reduce(add),
console.log,
);
해당 코드에서 map, filter는 Generator 를 사용하였기 때문에 take가 3개를 만족하게 되면 더이상 실행되지 않는다.
4. 함수형의 장점
- 가독성이 좋음
- 재사용성이 좋음
- 수정이 용이함