본문 바로가기

Javascript

JS - 제너레이터를 이용한 홀수 반환 함수 - JS ES6+ 함수형 프로그래밍 - 9

시리즈는 인프런 강의 함수형 프로그래밍과 JavaScript ES6+ ( 지식 공유자 : 유인동 ) 강의를 수강하면서 내용을 방식대로 포스팅하는 글입니다.

 

함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의

ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,

www.inflearn.com

 

제너레이터를 활용해 홀수만 계속해 발생시키는 이터레이터를 만들어서 순회하는 함수를 만들어보자

function* odds() {
  yield 1;
  yield 3;
  yield 5;
  yield 7;
}

let iter2 = odds();

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: 7, done: false}
console.log(iter2.next()); // {value: undefined, done: true}

... 이렇게 코드를 작성하면 알고리즘 따윈 하나 없는 코드가 ... 된다
물론 우리가 구현하려는 함수가 이런식으로 작동해야한다는 예제가 될수 있다.
이것을 자동화 해보자

// limit 값을 인자로 받은 것이다.
// 2씩 증가하도록 하는것이 더 효율적 이지만, 지금은 제너레이터의 사용법에 좀더 집중해보자
// 제너레이터 내부에서 어떠한 로직으로 값을 발생시키는 것을 제어할수가 있다
function* odds(limit: number) {
  for (let i = 0; i < limit; i++) {
    if (i % 2) yield i;
  }
}

let iter2 = odds(10);

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: 7, done: false}
console.log(iter2.next()); // {value: 9, done: false}
console.log(iter2.next()); // {value: undefined, done: true}

이제 위 함수는 10 까지 홀수만을 반환하는 제너레이터가 되었다.

이번엔 좀더 재밌게, 좀더 많은 제너레이터를 활용하는 함수를 만들어 보자.

값을 무한히 증가시키는 제너레이터를 구현해보자


// 이 제너레이터는 0 부터 시작해서, 또는 넘겨준 값으로 시작해서
// 무한히 값을 증가시켜주며 값을 발생시키는 함수를 만들었다고 가정해보자
function* infinity(i = 0) {
  while (true) yield i++;
}

let infinityIter = infinity();

console.log(infinityIter.next()); // {value: 1, done: false}
console.log(infinityIter.next()); // {value: 2, done: false}
console.log(infinityIter.next()); // {value: 3, done: false}
console.log(infinityIter.next()); // {value: 4, done: false}
console.log(infinityIter.next()); // {value: 5, done: false}
console.log(infinityIter.next()); // {value: 6, done: false}
console.log(infinityIter.next()); // {value: 7, done: false}
console.log(infinityIter.next()); // {value: 8, done: false}
// ...
// 이 infinity 제너레이터는 직접 next() 를 할때만 값이 증가하기 때문에,
// for...of 문에 사용하지 않는이상 브라우저가 멈추거나 프로그램이 죽는 일은 없다.
// 이런식으로 무한 수열을 표현할수 있다.

위에서 홀수만을 생성했던 제너레이터를 좀더 재밌게 변형해보자

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

function* odds(limit: number) {
  for (const idx of infinity(1)) {
    if (idx % 2) yield idx;
    if (idx === limit) return;
  }
}

let oddsIter = odds(10);

console.log(oddsIter.next()); // {value: 1, done: false}
console.log(oddsIter.next()); // {value: 3, done: false}
console.log(oddsIter.next()); // {value: 5, done: false}
console.log(oddsIter.next()); // {value: 7, done: false}
console.log(oddsIter.next()); // {value: 9, done: false}
console.log(oddsIter.next()); // {value: undefined, done: true}

제너레이터에 제너레이터를 실행시켜 무한히 홀수만을 반환하는 제너레이터가 되었다 !

여기에 또다른 제너레이터를 하나 더 추가시켜 보자

이번엔 limit 을 설정하는 제너레이터를 만들어보자

// 이 함수는 이터러블을 받아서, 계속해서 안에 있는 값을 yield 를 하다가
// 받아둔 limit 값을 만나면 더이상 코드가 실행되지 않도록 동작하는 함수다.
function* limit(limit: number, iter) {
  for (const idx of iter) {
    yield idx;
    if (idx === limit) return;
  }
}
let limitIter = limit(4, [1, 2, 3, 4, 5, 6])

console.log(limitIter.next()) // {value: 1, done: false}
console.log(limitIter.next()) // {value: 2, done: false}
console.log(limitIter.next()) // {value: 3, done: false}
console.log(limitIter.next()) // {value: 4, done: false}
console.log(limitIter.next()) // {value: undefined, done: true}

이제 이 제너레이터들을 모두 조합해서 아까와 같은 동작을 하게 할수 있다.

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

function* limit(limit: number, iter) {
  for (const idx of iter) {
    yield idx;
    if (idx === limit) return;
  }
}

function* odds(limitNum: number) {
  for (const idx of limit(limitNum, infinity(1))) {
    if (idx % 2) yield idx;
  }
}

let oddsIter = odds(10);

console.log(oddsIter.next()); // {value: 1, done: false}
console.log(oddsIter.next()); // {value: 3, done: false}
console.log(oddsIter.next()); // {value: 5, done: false}
console.log(oddsIter.next()); // {value: 7, done: false}
console.log(oddsIter.next()); // {value: 9, done: false}
console.log(oddsIter.next()); // {value: undefined, done: true}

for (const idx of odds(40)) console.log(idx); // 1 3 5 7 9 ... 39

제너레이터 코드를 활용하여 이렇게 다양한 로직들을 만들수 있다.

이렇게 문장으로 만들어 진것들을 순회시키거나 사용할수 있도록 으로 문장을 다룰 수 있다.

이런 코드 작성 방식은 JS 함수형 프로그래밍에서 좋은 기반이 된다.