타입스크립트 기초 - 11

Stulta Amiko·2022년 7월 5일
0

타입스크립트 기초

목록 보기
11/24
post-thumbnail

Iterator/Generator

어제 하다만 이터레이터 이야기를 좀더 해야한다.

이터레이터는 값이 필요한 시점에 생성하지만 일반 range 함수는 값이 필요한 시점보다 그 이전에 미리 생성한다는 차이가 있고
이로인한 메모리 효율성에서 차이가 생기게된다는 것이다.

앞에서 구현한 range 함수를 for..of에 넣게되면 정상적으로 작성하게 된다.

const range = (from: number,to: number): number[] => 
    from < to ? [from,...range(from+1,to)] : []

for(let value of range(1,4))
    console.log(value)

하지만 앞에서 작성한 createRangeIterable을 사용하면
오류가 발생하게 된다.
반복기를 반환하는 이터레이터 메서드가 있어야 한다고 오류를 띄운다.

class RangeIterable{
    constructor(public from: number,public 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{value,done}
            }
        }
    }
}

const iterator = new RangeIterable(1,4)

for(let value of iterator){
    console.log(value)
}

그럴때는 클래스를 만들어줘서 Symbol.iterator() 메서드를 만들어 주면 오류가 나지않고
정상적으로 잘 작동하는 모습을 볼 수 있다.

iterable<T>/iterator<T>

먼저 iterable은 자신을 구현하는 클래스가 Symbol.iterator를 제공하는것을 명확하게 알려준다.

iterator는 생성할 값의 타입을 명확하게 해준다.

class StringIterable implements Iterable<string>{
    constructor(private strings: string[] = [] ,private currentIndex: number = 0) { }
    [Symbol.iterator]() : Iterator<string> {
        const that = this
        let currentIndex = that.currentIndex, length = that.strings.length

        const iterator: Iterator<string> = {
            next(): {value: string,done: boolean} {
                const value = currentIndex < length ? that.strings[currentIndex++] : undefined
                const done = value == undefined

                return {value,done}
            }
        }
        return iterator
    }
}

for(let value of new StringIterable(['hello','world','!']))
    console.log(value)

예제에는 이런식으로 나와있다.

위에서 구현한 클래스와 큰 차이점은 존재하지 않는다.
좀더 명시적으로 구현한점이 차이점 인것같다.

Generator

function *키워드로 yield를 호출할 수 있고
yield는 return 과 유사하다

function* gen(){
    console.log("generator start")
    let value = 1
    while(value < 4)
        yield value++
    console.log('generator end')
}

for(let value of gen()){
    console.log(value)
}

실행결과
generator start
1
2
3
generator end

분명 코드를 이런식으로 짜지않았는데 신기하게도 위와같은 방식으로 작동한다.
생성기가 동작하는 방식을 세미코루틴이라고 한다.

단일스레드로 동작하는게 아닌 다중 스레드로 동작하는것 처럼 보이게한다.

위에서 구현한 string Iterator의 경우 생성기를 이용하면
더욱 간단하게 사용할 수 있다.

class IterableGen<T> implements Iterable<T>{
    constructor(private values: T[] = [],private currentIndex: number = 0) {}
    [Symbol.iterator] = function*(){
        while(this.currentIndex<this.values.length)
            yield this.values[this.currentIndex++]
    }
}

for(let item of new IterableGen([1,2,3])){
    console.log(item)
}

for(let item of new IterableGen(['hello','world','!'])){
    console.log(item)
}

function 키워드 뿐만아니라 yield 키워드도 있는데
원래 yield가 값을 대상으로 하는거라면
yield*는 객체나 배열을 대상으로 하는것이다.

0개의 댓글