Develop/JavaScript

이터레이터 & 제네레이터 ( iterator & generator )

KimBangg 2021. 8. 9. 21:45

1. ES5의 리스트 순회

  • 일반적인 프로그래밍 언어와 같이 숫자라는 "키"로 순회 하였습니다.
const list = [1,2,3];

for ( var i = 0; i < list.length; i++ ) {
    console.log(list[i]);
}

   

   

  

2. ES6의 리스트 순회

  • ES6 에서는 가독성을 위해 for of의 사용을 권고합니다.
  • 하지만 일반적인 경우와는 달리 "키"가 없는데, 어떤 방식으로 순회를 하는걸까요?
// 훨씬 간결하고, 선언적인 순회 방법
const ( const val of list ) {
    console.log(val);
}

   

   

  

3.  [ Array, Set, Map  ] 을 통해 알아보는 이터러블 & 이터레이터

 

  • 3-1 이터러블 & 이터레이터 이란?
    - 자바스크립트의 [ 배열, 객체, 맵 ] 은 이터러블, 이터레이블 프로토콜을 따르는 방식으로 구현이 되어있습니다.
    • - 이터러블 ( iterable ) -
      • 이터레이터를 관리하는 [Symbol.iterator()] 를 가진 값. 
    • - 이터레이터 ( iterator )
      • - { value, done } 객체를 리턴하는 next()를 가진 값.
    • - 이터레블 / 이터레이터 프로토콜
      • 이터러블을 for .. of, 전개 연산자 등과 함께 동작 하도록 하는 규약
  • 3-2 자바스크립트 배열, 맵, 셋의 작동 방식
    • 아래의 예시를 보면 arr는 key로 접근이 가능하지만, set과 Map은 key로 접근이 불가능합니다.
    • 즉, 이 사실을 통해 for.. of는 키를 바탕으로 값에 접근 하지 않는다는 것을 알 수 있습니다.
    •  // Array [ 키로 접근해서 순회가 가능 => console.log(a[0]) ]
        
        const arr  = [1,2,3];
        
        for ( const val of arr ) {
        console.log(val);  
        }
        
        // set [ 반면에 Set & Map 은 키로 접근이 안된다. ]
        
        const set = new Set([1,2,3]);
        
        console.log(set[1]); // undefined
      
        for ( const ele of set ) {
        console.log(ele);
        }
        
        // Map
        
        const map = new Map([['a',1], ['b',2], ['c', 3]]);
        
        console.log(map[1]); // undefined
      
        for ( const obj of map ) {
        console.log(obj);
        }
  • 작동 방식
    • 그렇다면 어떻게 동작하는걸까요?
    • arr, map, set은 모두 이터레이블 프로토콜을 기반으로 작동합니다.
      • const arr  = [1,2,3];
        arr[Symbol.iterator] = null;
        
        // output: TypeError: arr is not iterable
        for ( const val of arr ) {
          console.log(val);  
         }
    • 이터러블 프로토콜은 next를 포함하는 iterator 메소드로 구성이 되어있습니다.
    • 그리하여, 값이 있는 경우 done : false & value 가 전달이 되고 없는 경우는 done : true 값이 전달됩니다.
      • let iterator = arr[Symbol.iterator]();
        iterator.next() // { value : 1, done : false }
        iterator.next() // { value : 2, done : false }
        iterator.next() // { value : 3, done : false }
        iterator.next() // { value : undefined, done : true }
      • const arr1 = [1,2,3];
        let iter1 = arr[Symbol.iterator]();
        iterator.next() // { value : 1, done : false }
        iterator.next() // { value : 1, done : false }
        
        for ( const a of iter1 ) {
          console.log(a); // 3
        }  

   

  

4. 사용자 정의 이터러블

  • 이터러블에 대한 보다 자세한 이해를 위해 하단의 코드를 작성 해보았습니다.
  • 이터레이블 내부에는 총 길이, next(), iterator 를 포함 하고 있습니다.
const iterable = {
    [Symbol.iterator]() {

        let i = 3 // parameter.legnth

        return {
            //  next는 인덱스와 done 여부를 반환하는 함수입니다.
            next() {
                return i === 0 ? {done : true}  : { value  : i-- , done : false }
            },

            // well-formed iterable 은 iterataor를  통해서 모두 구동이 가능 해야 합니다.
            [Symbol.iterator]() {
                return this;
            }
        }
    }
}

let iterator = iterable[Symbol.iterator]();

// iterator & iterator 모두 구동 가능
for ( const a of iterator ) {
    console.log(a);
}

   

   

  

 

5. 전개 연산자

const a = [1,2];
// a[Symbol.iterator] = null;
console.log([...a, ...[3,4]]);
console.log(a);

   

   

  

6. 제너레이터

  • 제너레이터는 이터레이터이자 이터레이터를 생성하는 함수 입니다.
  • 제너레이터는 순회할 값을 문장으로 표현하는 것이라고도 말할 수 있습니다.
function *gen() {
    yield 1;
    yield 2;
    yield 3;

    return 100;
}

let iter = gen();
iter[Symbol.iterator]();
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 : ture }

for ( const a of gen() ) console.log(a)

   

   

  

7.  제너레이터 & 이터레이터 ( odds )

  • 제너레이터를 다음과 같이 응용하여 사용 할 수 있습니다.
function *limit(l, iter) {
    for ( const a of iter ) {
         yield a;
         if ( a === l ) {
            return;
        }
}

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

function *odds(ele) {

    for ( const a of limit( ele, infinity(1)) ) {
         if ( a % 2 ) {
            yield i;
        }

       // if ( a === ele ) return;
    }

}

let iter2 = odds(10);
console.log(iter2.next());
console.log(iter2.next());


// 평가 할 때만, 생성이 되기 때문에 브라우저가 멈추는 일은 없음.
let iter3 = infinity();
console.log(iter3.next());
console.log(iter3.next());
console.log(iter3.next()); 

let iter4 = limit(4, [1,2,3,4,5,6]);
// ...

for ( const a of odds(40) ) console.log(a);

   

   

  

8. 구조 분해, Spread

 

console.log(...odds(10));
console.log(...odds(10), ...odds(20));

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

const [a, b, ...rest ] = odds(10);
console.log(a);
console.log(b);
console.log(rest);