함수형 프로그래밍 3 generator와 iterator

함수형 프로그래밍의 필수개념인 generator에 대해 알아보았다.

generator

generator는 iterator이자 iterable을 생성하는 함수이다.

  • 함수를 선언할 때 함수명 앞에 *를 붙여 만든다
  • yield를 통해 iterator를 반환한다.
  • generator를 이용하며 어떤 값이든 순회할 수 있는 형태로 작성할 수 있다.
  • generator 내부 반환되는 값은 done:true일 때의 value로 반환된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function* gen() {
yield 1;
if (false) yield 4;
// 출력이 이루어지지 않는다.
yield 2;
yield 3;
return 100;
// 반환값은 순회할 경우 출력되지 않는다. done이 true이기 때문이다.
}

let iter = gen();
console.log(iter[Symbol.iterator]() == iter); // true
console.log(iter.next()); // {value:1,done:false}
console.log(iter.next()); // {value:2,done:false}
console.log(iter.next()); // {value:3,done:false}
console.log(iter.next()); // {value:100,done:true}

for (const a of gen()) console.log(a);
// 1
// 2
// 3

generator를 작성하는 여러가지 방법

아래의 oods 함수는 홀수를 value로 하는 iterable을 반환하는 generator이다.

1
2
3
4
5
6
7
8
9
10
11
function* oods() {
yield 1;
yield 3;
yield 5;
}

let iter2 = oods();
console.log(iter2.next()); // {value:1,done:false}
console.log(iter2.next()); // {value:3,done:false}
console.log(iter2.next()); // {value:5,done:false}
console.log(iter2.next()); // {value:undefined,done:true}

generator는 for문을 이용하여 작성할 수 있다.

1
2
3
4
5
6
7
8
9
10
function* oods(l) {
for (let i = 0; i < l; i++) {
if (i % 2) yield i;
}
}
let iter2 = oods(6);
console.log(iter2.next()); // {value:1,done:false}
console.log(iter2.next()); // {value:3,done:false}
console.log(iter2.next()); // {value:5,done:false}
console.log(iter2.next()); // {value:undefined,done:true}

또한 generator는 while을 이용하여 작성할 수 있다.

아래의 infinity는 무한수열을 만드는 generator이고 limit 함수는 l까지는 a를 yield하다가 l이 될 경우 return 하는 generator이다.
infinity generator와 limit generator를 이용하여 odds generator도 새로 정의하였다.

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
function* infinity(i = 0) {
while (true) yield i++;
}

function* limit(l, iter) {
for (const a of iter) {
yield a;
if (a == l) return;
}
}

function* oods(l) {
for (const a of limit(l, infinity(1))) {
if (a % 2) yield a;
}
}

let iter4 = oods(6);
iter3.next(); // {value:1,done:false}
iter3.next(); // {value:3,done:false}
iter3.next(); // {value:5,done:false}
iter3.next(); // {value:undefined,done:true}

for (const a of oods(6)) console.log(a);
// {value:1,done:false}
// {value:3,done:false}
// {value:5,done:false}
// {value:undefined,done:true}

iterable/iterator 프로토콜

generator는 iterable/iterator 프로토콜을 따르고 있기 떄문에 iterable/iterator 프로토콜을 따르는 여러 연산이나 함수들을 적용할 수 있다.

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
function* infinity(i = 0) {
while (true) yield i++;
}

function* limit(l, iter) {
for (const a of iter) {
yield a;
if (a == l) return;
}
}

function* oods(l) {
for (const a of limit(l, infinity(1))) {
if (a % 2) yield a;
}
}

console.log(...odds(10));
// 1 3 5 7 9
console.log([...oods(10), ...oods(20)]);
// 1 3 5 7 9 1 3 5 7 9 11 13 15 17 19

const [head, ...tail] = oods(5);
console.log(head);
// 1
console.log(tail);
// 3 5

const [a, b, ...rest] = oods(10);
console.log(a);
// 1
console.log(b);
// 3
console.log(rest);
// 5 7 9

Ref

함수형 프로그래밍과 JavaScript ES6+

댓글