본 포스트는 인프런의 함수형 프로그래밍과 JavaScript ES6+ 강의(링크)를 듣고 정리한 내용입니다.
const range = (l) => {
let i = -1;
let res = [];
while (++i < l) {
res.push(i);
}
return res;
};
console.log(range(5));
// [0, 1, 2, 3, 4]
console.log(range(2));
// [0, 1]
var list = range(4);
console.log(list); //(4) [0, 1, 2, 3]
console.log(reduce(add, list)); //6
range
를 직접 만들어 보았다.L.range = function* (l) {
let i = -1;
while (++i < l) {
yield i;
}
};
var list = L.range(4);
console.log(list); //L.range {<suspended>}
console.log(reduce(add, list)); //6
L.range
를 만들어 보았다.reduce
함수가 실행된다.//range
const range = (l) => {
let i = -1;
let res = [];
while (++i < l) {
console.log(i, "range");
res.push(i);
}
return res;
};
//0 'range'
//1 'range'
//2 'range'
//3 'range'
var list = range(4);
console.log(list); //(4) [0, 1, 2, 3]
//L.range
const L = {};
L.range = function *(l) {
let i = -1;
while (++i < l) {
console.log(i, "L.range");
yield i;
}
};
//출력 X
var list = L.range(4);
console.log(list); //L.range {<suspended>}
console.log(list.next());
//0 'L.range'
//{value: 0, done: false}
console.log(list.next());
//1 'L.range'
//{value: 1, done: false}
console.log(list.next());
//2 'L.range'
//{value: 2, done: false}
console.log(list.next());
//3 'L.range'
//{value: 3, done: false
range
의 경우 list를 선언하자 마자 평가가 된다.reduce
의 인자로 들어갈 경우, 내부에서 이터레이터로 변환을 거친 후 순회 한다.L.range
는 list를 선언한 직후에 평가되지 않았다.L.range
는 next()
를 호출 해야지만 한칸씩 순회한다.function test(name, time, f) {
console.time(name);
while (time--) f();
console.timeEnd(name);
}
test("L.range", 10, () => reduce, L.range(10000000)); //L.range: 0.013916015625 ms
test("range", 10, () => reduce, range(10000000)); //range: 0.070068359375 ms
L.range
는 평가되지 않은 상태이기 때문에 reduce
를 실행 할때 이터레이터로 변환을 거치지 않고 실행되기 때문에 위와 같은 좋은 효율성을 보인다.const take = (limit, iter) => {
let res = [];
for (const a of iter) {
res.push(a);
if (res.length == limit) return res;
}
return res;
};
console.log(take(5, range(100)));
//(5) [0, 1, 2, 3, 4]
console.log(take(5, L.range(100)));
//(5) [0, 1, 2, 3, 4]
take
라는 함수를 만들어 봤다.console.time("");
console.log(take(5, range(10000000)));
console.timeEnd("");
//(5) [0, 1, 2, 3, 4]
//137.99609375 ms
console.time("");
console.log(take(5, L.range(10000000)));
console.timeEnd("");
//(5) [0, 1, 2, 3, 4]
//0.22607421875 ms
range
는 10000000개의 배열을 만든 후, 그 배열을 이터레이터로 바꿔서 실행되므로 5개만 하는데도 오랜시간이 걸린다.L.range
는 5번의 next()
만 실행하면 되기 때문에 엄청나게 시간이 적게 걸린다.L : Lazy 지연성
L.map = function *(f, iter) {
for (const a of iter) yield f(a);
};
var it = L.map((a) => a + 10, [1, 2, 3]);
console.log(it.next()); //{value: 11, done: false}
console.log(it.next()); //{value: 12, done: false}
console.log(it.next()); //{value: 13, done: false}
map
을 만들어주는 함수이다.L.filter = function* (f, iter) {
for (const a of iter) {
if (f(a)) yield a;
}
};
var it = L.filter((a) => a % 2, [1, 2, 3, 4]);
console.log(it.next()); //{value: 1, done: false}
console.log(it.next()); //{value: 3, done: false}
console.log(it.next()); //{value: undefined, done: true}
filter
의 결과를 지연성을 가진 형태로 만들어주는 함수이다.go(
range(10),
map((n) => n + 10),
filter((n) => n % 2),
take(2),
console.log
);
//(2) [11, 13]
go(
L.range(10),
L.map((n) => n + 10),
L.filter((n) => n % 2),
take(2),
console.log
);
//(2) [11, 13]
take
-> L.filter
-> L.map
-> L.range
-> L.map
-> L.filter
-> take
순으로 들어갔다 나오면서 1회의 iterator.next()
에 대해서 모든 처리를 실행 한 후 다음 iterator.next()
에 대해서 함수들의 처리를 실행 해준다.(이후 done: true
가 될때까지 순회)take
처럼 모든 값을 순회 할 필요 없이 특정 조건을 만족 시켰을때 끝나는 함수와 사용할 때 시간적인 효울이 증가한다.사용하는 데이터가 무엇이든지
사용하는 보조 함수가 순수 함수라면 무엇이든지
아래와 같이 결합한다면 둘 다 결과가 같다.
[[mapping, mapping], [filtering, filtering], [mapping, mapping]]
[[mapping, filtering, mapping], [mapping, filtering, mapping]]
세로 방향 평가 == 가로 방향 평가
ㅎㅇㅎㅇ