함수형프로그래밍이 아직도 아직도 어렵지만, 재밌는 개념들이 많은 것 같습니다.
(유인동 개발자님 감사합니다...)
이번에는
숫자를 받고, 그숫자만큼의 배열을 리턴하는 range 함수를 만들어 보겠습니다.
const range = l => {
let i = 0;
const res = [];
while (i++ < l) {
res.push(i)
}
return res;
}
// range(5) => [1, 2, 3, 4, 5]
이번엔 해당 배열의 모든 값을 더해보겠습니다.
- 이때 reduce를 사용할건데, 기존 js Array.prototpe.reduce 말고 이터레이터를 순회하는 reduce를 만들어 사용하겠습니다.
const reduce = (fn, acc, iter) => {
// reduce (add, [1, 2, 3, 4, 5]) 넣어도 reduce (add, 1, [2, 3, 4, 5])로 변환
if (!iter) {
iter = acc[Symbol.iterator](); // 이터레이터로 만들고
acc = iter.next().value; // 첫번째 값 빼기
}
for (const a of iter) {
acc = fn(acc, a); // acc값 갱신
}
return acc;
}
const add = (a, b) => a+b;
const list = range(5); // [1, 2, 3, 4, 5]
reduce(add, list); // 15
우리가 원하는 대로 잘 동작이 되는군요!
이번엔 느긋한 range를 만들어보겠습니다 (지연평가를 말합니다)
const L = {};
L.range = function *(l) {
let i = 0;
while (i++ < l) {
yield i;
}
}
const add = (a, b) => a+b;
const list = L.range(5); // > L.range {<suspended>}
reduce(add, list); // 15
이번에는 range를 제너레이터로 만들었습니다.
과연 어떤 차이가 있을까요?
결론 부터 말하면,
range의 경우 배열을 다 만들고 순회를 하는 반면,
L.range의 경우 이터레이터를 순회하고 있습니다. (yield 할때 하나씩 꺼내옴, 배열을 만들지 않고 reduce로 들어가서 순회를 시작할때값이 평가됨)
미미할 수도 있지만 이는 효율성의 이점도 가져올 수 있습니다.
테스트를 해보겠습니다.
function timeTest(name, time, f) {
console.time(name);
while(time --) f();
console.timeEnd(name);
}
timeTest('range', 10, () => reduce(add, range(100000)));
timeTest('L.range', 10, () => reduce(add, L.range(100000)));
// range: 108.013916015625 ms
// L.range: 31.65576171875 ms
range보다 L.range가 3배보다 조금 더 빠른걸 알 수 있습니다.
'javascript > 함수형프로그래밍과 ES6' 카테고리의 다른 글
Iterator 과 iterable, iterable 프로토콜에 대하여 (0) | 2021.07.25 |
---|