Week2. range, 느긋한 L.range, take, L.map 등 전반적인 지연성 정리
인자에 길이(len)를 넣으면, 0부터 해당 길이만큼의 숫자를 담은 배열을 리턴.
const range = len => {
const result = [];
let i = -1;
while (++i < len) {
result.push;
}
return result;
}
const add = (a, b) => a + b;
console.log(range(5)) // [0, 1, 2, 3, 4]
console.log(range(3)) // [0, 1, 2]
console.log(reduce(add, range(5))) // 10
reduce
순서: arr를 만들고 👉🏻 iterator를 만들고 👉🏻 그 다음 next()로 순회.
generator로 만든 range.
const L = {};
L.range = function *(len) {
let i = -1;
while (i < len) {
yield i; // iterator를 만듦.
}
}
console.log(L.range(5)) // L.range {<suspended>}
console.log(reduce(add, L.range(5))) // 10
L.range
: arr를 만들지 않고, 실행될 때 하나씩 next()로 값을 꺼냄. 좀더 효율적임!
range와 L.range의 차이는, L.range
는 미리 arr를 만들지 않고, 필요할 때 iterator.next()로 원하는 수만큼 꺼내기 때문에 지연성은 시간적으로 효율적.
많은 값을 받아서 잘라주는 함수.
const take = (limit, iterable) => {
const result = [];
for (const value of iterable) {
result.push(value);
if (result.length === limit) return result;
}
return result;
}
console.log(take(5 ,range(100))) // [0, 1, 2, 3, 4]
console.log(take(5 ,L.range(100))) // [0, 1, 2, 3, 4]
iterable
을 받아 내부를 순회하며 일정 limit가 되면 잘라줌.
take
함수는 iterable 프로토콜
을 따르기 때문에 L.range
와 같이 지연성을 갖는 함수를 iterator로 만들면, 사용할 수 있다는 장점. 👍
예를 들어 이런 것도 가능.
console.log(take(5, range(Infinity)))] // 브라우저가 뻗음.
console.log(take(5, L.range(Infinity)))] // [0, 1, 2, 3, 4]
조건에 맞는 첫번째 값 반환
const find = curry((func, iterable) => go(
iterable,
L.filter(func), // L.filter로 지연성을 주면 필요한 만큼만 순회가능.
take(1),
([value]) => value
))
const users = [
{age: 30},
{age: 31},
{age: 28},
{age: 20},
{age: 33},
];
console.log(find(age => age < 30, users)); // {{age: 28}}
const join = (sep =',', iterable) =>
reduce((a, b) => `${a}${sep}${b}`, iterable);
const string = ['apple', 'juice'];
console.log(join('', string)) // 'applejuice'
가장 필요한 때까지 평가를 미루다가 필요할 때 해당 코드들을 평가하며 값을 만듦. (연산을 효율적으로 줄일 수 있다!)
iterable
중심 프로그래밍 === collection
중심 프로그래밍 === list
중심 프로그래밍앞서 구현했던 함수들을 지연성을 가진 함수로 다시 구현해보기.
L.map = function *(func, iterable) {
for (const value of iterable) yield func(value);
};
let iterator = L.map(value => value * 2 ,[1, 2, 3]); // 이땐 평가되지 않음.
console.log(iterator.next()); // {value: 2, done: false};
console.log(iterator.next()); // {value: 4, done: false};
console.log(iterator.next()); // {value: 6, done: false};
L.filter = function *(func, iterable) {
for (const value of iterable) {
if (func(value)) yield value;
}
}
let iterator = L.map(value => value > 1 ,[1, 2, 3]); // 이땐 평가되지 않음.
console.log(iterator.next()); // {value: 2, done: false};
console.log([iterator.next().value]); // [3]
iterable을 받아 펼친 iterator를 반환.
const isIterable = (a) => a && a[Symbol.iterator]; // a가 있을 경우 a[Symbol.iterator] 확인
L.flatten = function *(iterable) {
for (const a of iterable) {
if (isIterable(a)) for (const b of a) yield b;
else yield a;
}
}
const arr = [[1, 2], 3, 4, [5, 6, 7]];
const iterator = L.flatten(arr);
console.log(iterator.next()) // {value: 1, done: false}
console.log(...iterator) // [1, 2, 3, 4, 5, 6, 7];
L.flatten + L.map
이미 요소를 펼쳐주는 L.flatten을 구현했기 때문에, flatMap도 쉽게 구현 가능.
1. iterable을 mapping 함.
2. mapping 된 값을 flatten으로 펼쳐줌.
4. iterator를 리턴
L.flatMap = curry(pipe (
L.map,
L.flatten
));
go(
range(10),
map(num => num + 10),
filter(num => num % 2),
take(2),
console.log); // [11, 13]
한 문단의 평가가 다 처리돼야 다음 문단을 평가함. 만약 range(무한) 이라면 브라우저가 range에서 뻗어서 다음으로 넘어가지 못할 것임.
go(
L.range(10),
L.map(num => num + 10),
L.filter(num => num % 2),
take(2),
console.log); // [11, 13]
작동방식이 특이함. 가로가 아닌 세로로 진행됨.
range
,map
,filter
에서 평가를 미루고take(2)
에서 첫 시작.
take(2)
에서 인자로iterable
을 받으려 하니,그 iterable은filter에서 보낸 generator
.filter의 generator
에서for of
문을 순회하려고 하니 그때의 generator는map
꺼임.- map으로 올라가 순회하려고 하니 그때의 generator는
range
꺼임.range
에서 순회를 시작하고yield
값을 map으로 보냄.map
에서 mapping 후 yield 값을filter
로 보냄.filter
의 조건이 만족되지 않으면 take로 보내지지 않고 다음 yield를 받기 위해 똑같은 과정을 거쳐 range로 올라감.
.
.
.
반복filter
의 조건이 맞으면take
로 내려오고, take의 길이 조건이 맞으면 그대로 return.
TIL 정리 방법을 어떻게 해야 효율적인지 고민인데, 강의를 수강하면서 자세하게 적으니까 하루가 훌쩍 지나간다. 따라서 앞으로는 TIL에선 딱 필요한 정의만 간단 요약하고, 좀 더 깊게 공부하고 싶을 때 article 항목으로 빼보려고 한다. 강의와 TIL로 하루를 보내기엔 아무래도 과제, 스터디 발표, 개인 JS 공부, SCSS 공부가 쌓여있어서 ㅋㅋㅋ 어떤 게 효율적이고 나에게 도움이 될 지 계속 고민하는 시간을 가지자.