제너레이터를 활용해서 홀수만 계속해서 발생시키는 그러한 이터레이터를 만들어서 순회하는 예제를 작성한다.
우선 제너레이터 함수가 있다고 하자. 1 3 5 홀수만 생성한다.
<script>
function* odds() {
yield 1;
yield 3;
yield 5;
}
let iter2 = odds();
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
</script>
=>
{value: 1, done: false}
{value: 3, done: false}
{value: 5, done: false}
{value: undefined, done: true}
위에 코드는 직접 입력을 해줘야만 계속해서 홀수를 생성할 수 있는데 좀더 자동화해서 만들어보면 limit값을 받고 해당하는 값을 받아서 최대 10보다 작은 홀수까지만 출력하는 제너레이터를 작성할 수 있다.
<script>
function* odds(l) {
for (let i = 0; i < l; i++) {
if (i % 2) yield i;
}
}
let iter2 = odds(10);
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
</script>
=>
{value: 1, done: false}
{value: 3, done: false}
{value: 5, done: false}
{value: 7, done: false}
{value: 9, done: false}
{value: undefined, done: true}
{value: undefined, done: true}
{value: undefined, done: true}
더 많은 제너레이터를 함께 사용하면서 만들어보자. infinity 제너레이터를 만들어보면 0부터 시작해서 혹은 넘겨준 값으로 부터 시작해서 무한히 i++하면서 해당하는 값을 계속해서 리턴한다고 가정해본다. infinity라는 제너레이터가 있으면 제너레이터를 통해서 생성하는 이터레이터는 next를 할 때마다 계속해서 생성할 것이다.
무한히 값을 생성하지만 사용자가 이터레이터의 next를 평가할 때 까지만 동작하기 때문에 while true인 코드를 작성한다고 해서 브라우저가 멈추거나 프로그램이 멈추는 일은 없다.
이런식으로 무한수열을 표현할 수 있다.
function *infinity(i = 0) {
while (true) yield i++;
}
let iter3 = infinity();
undefined
iter3.next()
{value: 0, done: false}
iter3.next()
{value: 1, done: false}
iter3.next()
{value: 2, done: false}
iter3.next()
{value: 3, done: false}
iter3.next()
{value: 4, done: false}
iter3.next()
{value: 5, done: false}
iter3.next()
{value: 6, done: false}
iter3.next()
{value: 7, done: false}
iter3.next()
{value: 8, done: false}
무한수열을 통해서 홀수만 뽑는 함수를 변형해보자. 아래와 같이 코드를 작성해도 정상적으로 작동한다.
function* infinity(i = 0) {
while (true) yield i++;
}
function* odds(l) {
for (const a of infinity(1)) {
if (a % 2) yield a;
if (a == l) return;
}
}
=>
{value: 1, done: false}
{value: 3, done: false}
{value: 5, done: false}
{value: 7, done: false}
{value: 9, done: false}
{value: undefined, done: true}
{value: undefined, done: true}
{value: undefined, done: true}
좀 더 변화를 주면 이번에는 limit을 만드는 제너레이터를 만들어서 l과 iter을 받도록 하자.
이 함수는 이터러블을 받아서 계속해서 이터러블 안에 있는 값을 yield하다가 받아둔 l과 같은 값을 만나면 더이상 돌지 않도록 동작할 것이다.
<script>
function* infinity(i = 0) {
while (true) yield i++;
}
function* limit(l, iter) {
for (const a of iter) {
yield a;
if (a == l) return;
}
}
function* odds(l) {
for (const a of limit(l, infinity(1))) {
if (a % 2) yield a;
}
}
let iter2 = odds(10);
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
log(iter2.next());
for (const a of odds(40)) log(a);
console.clear();
</script>
for...of, 전개 연산자, 구조 분해, 나머지 연산자
제너레이터는 이터러블 이터레이터 프로토콜을 따르고 있기 때문에
for of문이나 전개 연산자 구조 분해 나머지 연산자 등 자바스크립트에서 이터러블 프로토콜을 따르고 있는 문법들 혹은 이터러블 프로토콜을 따르고 있는 많은 라이브러리나 많은 함수들 헬퍼함수들과 함께 잘 사용될 수가 있다.
아래와 같이 코드를 작성할 수 있다. const [head, ...tail]과 같이 구조분해를 통해서도 사용할 수 있다.
자바스크립트에서는 이터러블 이터레이터 프로토콜을 가지고 활용할 수 있는 문법들과 기능들이 굉장히 많고 잘 동작하고 있다. 많은 라이브러리들이나 함수들도 이터러블 프로토콜을 따르도록 구성이 되어있다면 이렇게 제너레이터나 이터레이터등을 활용을 잘할때 좀 더 조합성이 높은 프로그래밍을 할 수 있다.
제너레이터와 이터러블과 이터레이터등을 많이 활용하여서 함수형 프로그래밍 관점에서 이것들을 해석해서 학습해보자.
<script>
log(...odds(10));
log([...odds(10), ...odds(20)]);
const [head, ...tail] = odds(5);
log(head);
log(tail);
const [a, b, ...rest] = odds(10);
log(a);
log(b);
log(rest);
</script>
=>
1 3 5 7 9
[1, 3, 5, 7, 9, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
1
[3, 5]
1
3
(3) [5, 7, 9]
<출처 : 유인동 함수형 프로그래밍과 JavaScript ES6+>
https://www.inflearn.com/course/functional-es6/dashboard
함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의
ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,
www.inflearn.com
'JavaScript > 함수형 프로그래밍과 JavaScript ES6+' 카테고리의 다른 글
이터러블 프로토콜을 따른 map의 다형성 (0) | 2021.08.18 |
---|---|
map, filter, reduce(map) (0) | 2021.08.17 |
제너레이터와 이터레이터 (0) | 2021.08.17 |
사용자 정의 이터러블, 이터러블/이터레이터 프로토콜 정의 & 전개 연산자 (0) | 2021.08.17 |
Array, Set, Map을 통해 iterable/iterator protocol 알아보기 (0) | 2021.08.17 |