일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- var
- 오버로딩
- golang
- Overloading
- Python
- javascript
- 코틀린
- adapter
- HP
- Java
- Android
- 연산자
- 노트북 추천
- js
- ListView
- 자바
- 배열
- 자바스크립트
- 리스트 뷰
- Array
- Kotlin
- as?
- 함수
- 패널 교체
- 파이썬
- 싱글 스레드
- 안드로이드
- go
- node.js
- 노트북
- Today
- Total
Bbaktaeho
[Javascript] Generator 활용과 장점 (iterable, iterator, lazy evaluation) 본문
[Javascript] Generator 활용과 장점 (iterable, iterator, lazy evaluation)
Bbaktaeho 2020. 10. 21. 00:22들어가며
generator, iterator, iterable의 상세한 설명은 없습니다.
궁금하신 분들은 아래의 포스팅을 먼저 읽고 보는 것을 추천드립니다.
무한 루프 (무한수열 만들기)
일반 함수에서 무한 루프가 발생한다면 프로세스가 죽을 수 있습니다.
function infinity() {
let i = 0;
while (true) console.log(++i);
}
위 함수에서 우리가 원할 때 증가한 i값을 출력하고 싶어도 그렇게 동작할 수 없습니다.
function infinity() {
let i = 0;
while (true) return ++i;
}
console.log(infinity()); // 1
console.log(infinity()); // 1
console.log(infinity()); // 1
꺼내올 때마다 계속 1만 꺼내올 것입니다.
하지만 제너레이터를 활용한다면 증가하는 수를 원할 때 꺼내올 수 있게 됩니다.
먼저 제너레이터를 만들어줍니다.
function* infinity() {
let i = 0;
while (true) yield ++i;
}
제너레이터는 이터레이터를 만드는 함수로서 이터레이터의 next()를 활용하여 값을 지연 평가할 수 있게 됩니다.
여기서 지연 평가란, 로직에서 뒤늦게 값이 필요할 때 만들어내는 방식입니다.
이터레이터를 가지고 무한수열을 만들어보겠습니다.
function* infinity() {
let i = 0;
while (true) yield ++i;
}
const iter = infinity();
console.log(iter.next().value); // 1
console.log(iter.next().value); // 2
console.log(iter.next().value); // 3
실행 결과는 1, 2, 3 으로 정상 동작합니다.
마치 데이터베이스의 Auto Increment처럼 우리가 원할 때 iter만 공유한다면 언제든지 1이 증가된 수를 받아서 사용할 수 있게 됩니다.
하지만 스프레드 연산자나 for of 문을 그대로 실행시키면 모두 평가되어 무한 루프가 발생하므로 주의해야 합니다.
지연 평가의 장점
앞서 언급한 지연 평가를 좀 더 확실히 경험해 보겠습니다.
1부터 99까지 순회할 수 있는 반복자를 만들어 보겠습니다.
먼저 배열을 만드는 방법입니다.
function newArr(n) {
let i = 1;
const res = [];
while (i < n) res.push(i++);
return res;
}
const arr = newArr(100);
console.log(arr);
코드를 실행하면
[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
97, 98, 99
]
1부터 99까지 배열이 출력됩니다.
다음은 이터레이터 즉, 반복 가능한 객체를 만드는 방법입니다. (배열도 반복 가능한 객체입니다)
function* newArrGen(n) {
let i = 1;
while (i < n) yield i++;
}
const iter = newArrGen(100);
console.log(iter);
코드를 실행하면 (node.js)
Object [Generator] {}
object 타입이 출력된 것을 확인할 수 있습니다.
다시 말해 반복 가능한 객체가 만들어졌고 즉시 평가되지 않아서 1~99까지 리스트가 만들어지지 않았습니다.
이제 위의 두 함수를 가지고 만들어진 반복 가능한 객체로 5의 배수를 작은 수부터 2개만 찾도록 구현해보겠습니다.
function newArr(n) {
let i = 1;
const res = [];
while (i < n) res.push(i++);
return res;
}
function* newArrGen(n) {
let i = 1;
while (i < n) yield i++;
}
function fiveArr(iter) {
const res = [];
for (const item of iter) {
if (item % 5 == 0) res.push(item);
else if (res.length == 2) break;
}
return res;
}
console.log(fiveArr(newArr(100)));
console.log(fiveArr(newArrGen(100)));
실행 결과
[ 5, 10 ]
[ 5, 10 ]
같은 결과를 만들어냅니다. 하지만 제너레이터를 활용한 코드는 좀 더 빠르게 동작합니다.
이 차이가 즉시 평가와 지연 평가인데요.
fiveArr(newArr(100))
코드는 newArr 함수가 배열을 즉시 만들어냅니다. (1~99까지) 만들어진 배열을 리턴해서 fiveArr 함수를 수행하게 됩니다. 쉽게 말해 fiveArr([1, 2, 3, 4, 5, 6, ..., 97, 98, 99]) 된다는 것입니다.
fiveArr(newArrGen(100))
코드는 newArrGen 함수가 이터레이터만 만들어내고 fiveArr 함수에서 필요할 때 이터레이터에서 평가된 값을 사용하게 됩니다.
극단적으로 크기를 올려서 시간을 비교해보겠습니다.
console.time('');
console.log(fiveArr(newArr(10000000))); // [ 5, 10 ]
console.timeEnd(''); // : 285.535ms
console.time('');
console.log(fiveArr(newArrGen(10000000))); // [ 5, 10 ]
console.timeEnd(''); // : 7.296ms
어마어마한 크기의 반복 가능한 객체를 만든 다음에 fiveArr 함수를 실행하는 모습입니다.
그러나 즉시 평가와 달리 지연 평가는 확실히 빠르게 동작하는 것을 볼 수 있습니다.
newArrGen 함수에 log를 찍어보면 필요할 때만 값을 평가해서 동작하는 모습을 볼 수 있습니다.
function* newArrGen(n) {
let i = 1;
while (i < n) {
console.log(i);
yield i++;
}
}
...
...
...
console.time('');
console.log(fiveArr(newArrGen(10000000)));
console.timeEnd('');
while문 안에 log를 출력하도록 코드를 수정하고 실행합니다.
1
2
3
4
5
6
7
8
9
10
11
[ 5, 10 ]
: 11.452ms
아무리 큰 수까지 반복해도 결과적으로 필요할 때 평가되어 실행되므로 빠르게 동작할 수 있습니다.
log를 출력하느라 시간이 조금 더 걸린 것 같네요.
값이 필요할 때 이터레이터에서 꺼내 쓰므로 무한대로 이터레이터를 만들어도 결과는 같습니다.
console.time('');
console.log(fiveArr(newArrGen(Infinity))); // [ 5, 10 ]
console.timeEnd(''); // : 7.441ms
미완성
추후 더 좋은 예제와 정보로 다시 업데이트 하겠습니다.
'프로그래밍 (Programming) > 자바스크립트 (JavaScript)' 카테고리의 다른 글
[JavaScript] Object.assign 그리고 Spread (Object, 객체, 속성 복사, spread) (0) | 2021.05.11 |
---|---|
[Javascript] 기본 제공 API (자바스크립트 팁) (0) | 2020.10.24 |
[Javascript] Generator, Iterator, Iterable (제너레이터, 이터레이터, function*) (0) | 2020.10.17 |
[Javascript] null 병합 연산자 '??' 와 OR 연산자 '||' 의 차이 (자바스크립트, nullish coalescing, falsy값) (2) | 2020.08.24 |
[Javascript/Node.js] 싱글 스레드 프로그래밍 (uncaughtExcetion) (0) | 2020.05.26 |