ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JS_제너레이터
    TIL/프로그래머스 웹 데브코스 2021. 8. 10. 15:30
    728x90

    Generator

    이터레이자이자 이터레이터를 생성하는 함수를 말한다.

    즉, 제너레이터는 이터레이터를 리턴하는 함수이다.

    이때 이터레이터는 Well formed Iterator이다.

    또한, 제너레이터는 순회할 특정 값을 문장으로 표현하는 방법이자 함수라고 부르기도 한다.

    자바스크립트에서는 이터러블이면 순회할 수 있다.

    그런데, 제너레이터는 이러한 문장을 순회 할 수 있는 값으로 만들 수 있기 때문에 제너레이터를 통해서 어떠한 상태나 어떠한 값이든 순회 할 수 있게 만들 수 있다고 한다!!!

     

    제너레이터 함수를 사용하면 Iteration protocol (이터레이션 프로토콜 = 이터레이터/이터러블 프로토콜)을

    준수하며 보다 더 간편하게 이터러블을 구현할 수 있으며, 비동기 처리시 유용하게 사용된다.


    제너레이터 함수의 정의

    function* 키워드로 선언하며, 하나 이상의 yield 문을 포함한다.

    (여기서 yield 키워드는 제너레이터 함수를 중단하거나 재개하는 용도로 사용된다.)

    아래와 같이 제너레이터를 통해 

    it1, it2와 같이 간단하게 Iterator를 만들 수 있음을 확인할 수 있다.

    이렇게 반횐된 IteratorIterator이자 Iterable로서 Symbol.iterator 메소드를 가진다.

    즉 이렇게 반환된 Iterator는 well formed Iterator로

    제너레이터 함수는 well formed Iterator를 리턴하는 함수이다!

    function* gen(){
                yield 1;
            }
            
    function *gen2(){
                yield 2;
            }


    yield 

    해당 키워드는 제너레이터 함수를 중단하거나 재개하는데 사용된다.

    이를 이용해 yield로 몇 번의 next() 로 값을 꺼내줄 것인지를 제어할 수 있다.

    function* gen(){
                yield 1;
                yield 3;
                yield 5;
            }
            
    function* gen2(){
                yield 2;
                yield 4;
                yield 6;
                yield 8;
                yield 10;
            }


    제너레이터 결과로 순회하기

    제너레이터 함수의 실행 결과는 이터레이터이자 이터러블인 well formed iterator이기 때문에

    아래와 같이 순회를 할 수 있다.

    for (const a of gen()) console.log(a);
    console.log('---------------------');
    for (const a of gen2()) console.log(a);


    제너레이터 함수의 리턴 값

    제너레이터 함수의 리턴 값은 마지막 done : true가 되며 리턴에서 전달한 결과가 value에 들어가게 된다.

    여기서 중요한 것을 순회를 하는 경우 리턴 값은 사용되지 않고 순회가 이루어 진다는 것이다.

    function* gen(){
                yield 1;
                yield 3;
                yield 5;
                return 1000;
            }
            
            for (const a of gen()) console.log(a);


    제너레이터 홀수 출력 함수 구현 예제

    로직 상 비효율적이지만 어떻게 동작하는지 알기 위해 직관적인 예제를 들어본다!!

     

    yield 에 하나하나 값을 넣어 출력!

    function *odds(){
                yield 1;
                yield 3;
                yield 5;
            }
    
            let it3 = odds();
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());


    limit 값을 정하고 일반적인 for문을 적용하여 출력

    // limit 값 인자로 전달
            function *odds(limit){
                for (let i = 0; i < limit; i++){
                    if (i % 2) yield i;
                }
            }
    
            let it3 = odds(10);
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());


    무한 수열 표현

    next() 실행할때마다 무한히 값을 생성하는 것을 볼 수 있다.

    여기서 while은 흔히 말하는 무한 루프라고 생각할 수도 있지만,

    해당 함수의 Iterator를 next() 함수로 평가할때 까지만 동작하기 때문에

    while(true)라고 해서 브라우저, 프로그램이 멈추지 않는다!!

    이는 yield가 제너레이터 함수를 중지 및 재개 시키는 것과도 관련이 있다고 생각한다.

    // 무한 수열 표현
            // i == 0 부터 또는 넘겨받은 값부터 시작함
            function *infinity(i=0){
                while (true) yield i++;
            }
    
            let it2 = infinity(5);
            console.log(it2.next());
            console.log(it2.next());


    무한 수열과 limit 반복문 혼합사용

    무한 수열을 나타내는 함수를 이터레이터이자 이터러블로 아래와 같이 사용 가능하다

    // 무한 수열 표현
            // i == 0 부터 또는 넘겨받은 값부터 시작함
            function *infinity(i=0){
                while (true) yield i++;
            }
            
            // limit 값 인자로 전달
            function *odds(limit){
                for (const a of infinity(1)){
                    if (a % 2) yield a;
                    if (a == limit) return;
                }
            }
    
            let it3 = odds(10);
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());


    limit 값과 Iterable을 인자로 받는 함수

    Iterable을 계속 순회하면서 yield a를 실행하고 a가 limit 와 같아지는 종료!

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


    제너레이터 함수 연결하여 출력하기

    정의한 함수가 최소한의 책임과 일만하도록? 마치 뚱뚱한 인터페이스를 분리하는 것처럼

    각각의 함수를 역할에 맞게 분리하고 연결하는 형태로 쓸 수 있으며 함수의 재사용이 용이해질 수 있을 것 같다.

    function *infinity(i=0){
                while (true) yield i++;
            }
    
            
            function *limit(limit, iterable){
                for (const a of iterable){
                    yield a;
                    if (a == limit) return;
                }
            }
            
            function *odds(l){
                for (const a of limit(l, infinity(1))){
                    if (a % 2) yield a;
                }
            }
    
            let it3 = odds(10);
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            console.log(it3.next());
            
            
            
            for (const a of odds(40)) console.log(a);

    728x90

    'TIL > 프로그래머스 웹 데브코스' 카테고리의 다른 글

    JS_코드를 값으로 다루는 go, pipp, curry 함수  (0) 2021.08.12
    JS_Virtual DOM  (1) 2021.08.10
    JS_Iterable  (0) 2021.08.10
    JS_리스트 순회_Array, Set, Map  (0) 2021.08.09
    DOM(Document Object Model)  (0) 2021.08.06
Designed by Tistory.