JavaScript

[JavaScript] 함수형 프로그래밍 (제너레이터, 이터레이터)

swimKind 2022. 3. 31. 00:07

[JavaScript] 함수형 프로그래밍 (제너레이터, 이터레이터)

 

 

 

# 제너레이터 / 이터레이터

 - 제너레이터: 이터레이터이자 이터러블을 생성하는 함수

function* gen(){
  yield 1;
  if(false) yield 2;
  yield 3;
}

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

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

제너레이터는 이터러블과 이터레이터의 조건을 만족한다. 따라서 제너레이터 함수의 객체를 이터러블을 만족하는 작업 없이도 for...of 문으로 순회 할 수 있다. 또한 제너레이터는 순회할 값을 문장(yield)으로 표현할 수 있다. 어떠한 값이든 이터러블이면 순회할 수 있고, 제너레이터는 문장을 값으로 만들수 있고 문장을 통해 순회할 수 있는 값을 만들 수 있기때문에 어떠한 상태나 어떠한 값이든 순회할 수 있도록 만들 수 있다.

 

이 점은 함수형 프로그래밍의 관점에서 중요하다. 자바스크립트는 다형성이 높다고 할 수있다. 제너레이터라는 문장을 통해 순회할 수 있는 값을 만들 수 있다는 것은 어떠한 값도 순회할 수 있는 형태로 동작할 수 있고 다양한 값을 순회하는 이터러블을 만들 수 있다.

 

 

# odds

function* infinity(i = 0){
  while(true) yield i++;
}

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

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

let iter = odds(10);
iter.next(); // { value: 1, done: false }
iter.next(); // { value: 3, done: false }
iter.next(); // { value: 5, done: false }
iter.next(); // { value: 7, done: false }
iter.next(); // { value: 9, done: false }
iter.next(); // { value: undefined, done: true }
iter.next(); // { value: undefined, done: true }

for(const a of odds(40)) console.log(a); // 1, 3, 5 ... 39

제너레이터를 이용해서 홀수만을 출력하는 함수를 만들었다. infinity() 제너레이터함수의 경우는 무한하는 while 문에서 i의 값을 1씩 증가시킨다. 겉으로 보기엔 함수를 실행하면 브라우저가 다운되겠지만 제너레이터 함수의 yield를 통해서 하나씩 값을 실행하고 실행을 중지 시키기때문에 그렇게 되지는 않는다.

 

limit() 제너레이터함수는 파라미터로 숫자와 이터러블을 만족하는 값을 받아온다. 받아온 이터러블을 순회하면서 인자로 받은 숫자와 이터러블의 값이 같아질 때 리턴하여 done: true를 반환한다.

 

odds() 제너레이터 함수는 limit() 제너레이터함수와 infinity() 제너레이터함수를 사용하여 홀수 값을 가져온다. limit과 infinity는 이터러블 프로토콜을 만족하므로 for...of를 순회하는 이터러블 값으로 사용될 수 있다.

 

 

# for of, 전개 연산자, 구조 분해 할당, 나머지 연산자

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

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

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

자바스크립트에서는 이터러블 / 이터레이터 프로토콜을 가지고 활용할 수 있는 문법들과 기능들이 많다. 또한 많은 라이브러리나 함수들도 이터러블 프로토콜을 따르도록 구성이 되어있다면 제너레이터와 이터레이터를 활용하여 좀 더 조합성이 높은 프로그래밍을 할 수 있다.