Million Dreams
100만개의 꿈을 꾸는 개발자 지망생
Do it! 타입스크립트 프로그래밍 6장

Ch06 반복기와 생성기

6-1) 반복기 이해하기

(1) 반복기와 반복기 제공자

- for…of 구문은 타입에 무관하게 배열에 담긴 값을 차례로 얻는 데 활용되며, 다른 프로그래밍 언어에서도 반복기(iterator)라는 주제로 흔히 찾아볼 수 있음

 

- 반복기는 일반적으로 다음과 같은 특징이 있음

1. next라는 이름의 메서드를 제공

2. next 메서드는 valuedone이라는 두 개의 속성을 가진 객체를 반환

 

반복기제공자(iterable)의 예

export const createRangeIterable = (from: numberto:number) => {

    let currentValue = from

    return {

        next() {

            const value = currentValue < to ? currentValue++ : undefined

            const done = value == undefined

            return {valuedone}

        }

    }

}

 

(2) 반복기는 왜 필요한가?

- 반복기 제공자는 어떤 범위의 값을 한꺼번에 생성해서 배열에 담지 않고 값이 필요할 때만 생성함

 

-  직접 실습한 range 함수와 createRangeIterator 함수를 비교해보면 createRangeIterator는 값이 필요한 시점에 비로소 생성하지만, range 함순느 값이 필요한 시점보다 이전에 미리 생성한다는 차이점이 있다. 따라서 시스템 메로이 효율상 createRangeIterator 함수가 메모리를 훨씬 적게 소모함.

 

(3) for…of 구문과 [Symbol.iterator] 메서드

- range 함수는 for…of 구문의 of 뒤에 올 수 있지만 createRangeIterator는 똑같이 적용하면, ‘[Symbol.iterator]() 메서드가 없다라는 오류가 발생함

 

- 이를 개선해 다음과 같이 RangeIterable 함수를 만들 수 있음

export class RangeIterable {

    constructor(public from:numberpublic to: number) {}

    [Symbol.iterator]() {

        const that = this

        let currentValue = that.from

        return {

            next() {

                const value = currentValue < that.to ? currentValue++ : undefined

                const done = value == undefined

                return {valuedone}

            }

        }

    }

}

(4) Iterable<T>Iterator<T> 인터페이스

- 타입스크립트는 반복기 제공자에 Iterable<T>Iterator<T> 제네릭 인터페이스 사용 가능

Class 구현 클래스 implements Iterable<생성할 값의 타입> {}

- 또한 Iterator<T>는 반복기가 생성할 값의 타입을 명확하게 해줌

[Symbol.iterator](): Iterator<생성할 값의 타입> {}

 

Iterable<T>Iterator<T>를 사용해 구현한 예

export class StringIterable implements Iterable<string> {

    constructor(private strings: string[] = [], private currentIndex: 

number = 0) {}

    [Symbol.iterator](): Iterator<string> {

        const that = this

        let currentIndex = that.currentIndexlength = that.strings.length

 

        const iterator: Iterator<string= {

            next(): {value: stringdone: boolean} {

                const value = currentIndex < length ? 

that.strings[currentIndex++: undefined

                 const done = value == undefined

                  return {valuedone}

            }

        }

        return iterator

    }

}

 

6-2) 생성기 이해하기

- ESNext 자바스크립트와 타입스크립트는 yield라는 키워드를 제공

- yield return 키워드처럼 값을 반환하며, function* 키워드를 사용한 함수에서만 호출 가능하며 function*로 만든 함수를 생성기(generator)라고 함

생성기 예시

export function* generator() {

    console.log('generator started...')

    let value = 1

    while(value < 4)

        yield value++

    console.log('generator finished')

}

 

테스트 코드 예시

import {generatorfrom './generator'

for(let value of generator())

    console.log(value)

 

 (1) setInterval 함수와 생성기의 유사성

- 생성기가 동작하는 방식을 세미코루틴(semi-coroutine, 반협동 루틴)’이라고 하며, 세미코루틴은 타입스크립트처럼 단일 스레드(single-thread)로 동작하는 프로그래밍 언어가 마치 다중스레드(multi-thread)로 동작하는 것처럼 보이게 하는 기능을 함

 

- setInterval 함수의 동작 방식은 C++ 언어의 스레드 동작 방식과 흡사한 면이 있다.

 

Cf) 세미코루틴과 코루틴의 차이

- 반도체(semiconductor)에서 반도체란 전기를 절반만 통과시키는 도체라는 의미이며, 여기서 반은 반대(anti)의 의미가 아닌 절반(semi)의 의미임

- 이처럼 생성기를 세미코루틴(semi-coroutine)이라고 하는데 절반만 코루틴이라는 뜻이다.

- 1958년부터 꾸준히 학문적으로 연구해온 주제로 클로저(Clojure)는 코루틴을 최초로 프로그래밍 문법으로 탑재한 언어이며, 구글의 Go언어는 고루틴(Goruotine)이라는 용어를 쓰며 코루틴과 맥락을 같이한다.

- 코루틴은 애플리케이션 레벨의 스레드로, 과거에는 스레드 개수가 제한되어있어, 스레드를 과ㅅ다하게 소비하면 운영체제에 무리를 주기 때문에 코루틴을 연구하기 시작했다.

 

- 코루틴은 스레드이므로 일정 주기에 따라 자동으로 반복해서 실행되지만, 생성기는 절반만 코루틴이므로 반복해서 실행은 되지만 자동으로 실행되지 못하는 코루틴이다.

 

- 생성기는 사용하는 쪽 코드에서 생성기가 만들어 준 반복자의 next 메서드가 호출될 때만 한번 실행됨 즉, 자동으로 반복 실행되지 않으므로 세미코루틴이라고 함.

 

(2) function* 키워드

- function* 키워드로 선언된 함수가 생성기인데, 생성기는 오직 function* 키워드로만 만들 수 있으므로 화살표 함수로 생성기를 만들 수 없음.

 

주의할 점 : 생성자를 만들 때 선언하는 function* function*을 붙인게 아니라 function* 자체가 하나의 키워드다. 따라서 화살표 함수 형태로는 생성기를 만들 수 없으며, function* 사이에는 공백은 없어도 되고 있어도 상관 없다.

 

(3) yield 키워드

- yield는 연산자(operator) 형태로 동작하며 2가지 기능을 한다.

1. 반복기를 자동으로 만들어 준다

2. 반복기 제공자 역할도 수행한다

 

function* 키워드를 이용해 생성기 형태로 만든 함수 예

export function* rangeGenerator(from: numberto: number) {

    let value = from

    while(value < to) {

        yield value++

    }

}

 

(4) 반복기 제공자의 메서드로 동작하는 생성기 구현

- 생성기는 반복기를 제공하는 반복기 제공자로서 동작함

생성기를 사용해 구현한 코드 예

export class IterableUsingGenerator<Timplements Iterable<T> {

    constructor(private values: T[] = [], private currentIndex: number = 0) {}

    [Symbol.iterator= function* () {

        while(this.currentIndex < this.values.length)

            yield this.values[this.currentIndex++]

    }

}

 

(5) yield* 키워드

- 타입스크립트는 yield 뒤에 *을 붙인 yield* 키워드도 제공하며, yield는 단순히 값을 대상으로 동작하지만, yield*는 다른 생성기나 배열을 대상으로 동작함

 

(6) yield 반환값

- yield 연산자는 값을 반환함

 

* yield 반환 값 함수 예시

export function* gen() {

    let count = 5

    let select = 0

    while(count--) {

        select = yield `you select ${select}`

    }

}

export const random = (maxmin=0) => Math.round(Math.random() * (max-min)) + min

  Comments,     Trackbacks