티스토리 뷰

반응형

코틀린의 시퀀스

코틀린 시퀀스는 List나 set과 같은 컬렉션이랑 비슷한 개념이지만, 필요할 때마다 값을 하나씩 계산하는 지연(lazy) 처리를 합니다.

 

시퀀스 특징

 

  • 요구되는 연산을 최소한으로 수행한다.
  • 무한정 될 수 없다.
  • 메모리 사용이 효율적이다.
val seq = sequence {
    yield(1)
    yield(2)
    yield(3)
}

fun main() {
    for (num in seq) {
        println(num)
    }
}

 

sequence 함수는 짧은 DSL(Domain-Specific Language, 도메인 전용 언어)코드이다.

인자는 수신객체 지정 람다 함수이다.

(suspend SequenceScope<T>.() -> Unit)

람다 내부에서 수신 객체인 this는 SequenceScope<T>를 가리킨다.

 

이 객체는 yield 함수를 가지고 있다.

this가 암시적으로 사용되므로 yield(1)을 호출하면 this.yield(1)을 호출하는 것과 동일하다.

 

각 숫자가 미리 생성되는 대신, 필요할 때 마다 생성된다.

시퀀스 빌더 내부 그리고 시퀀스를 사용하는 곳에서 메시지를 출력하면 다음과 같다.

 

val seq = sequence {
    println("Generating first")
    yield(1)
    println("Generating second")
    yield(2)
    println("Generating third")
    yield(3)
    println("Done")
}

fun main() {
    for (num in seq) {
        println("The next number is %d".format(num))
    }
}

출력

Generating first
The next number is 1
Generating second
The next number is 2
Generating third
The next number is 3
Done

 

동작방식을 확인해보면,

  1. 첫 번째 수를 요청하면 빌더 내부로 진입
  2. "Generating First"를 출력한 뒤, 숫자 1을 반환.
  3. 반복문에서 반환된 값을 받은 뒤, "The next number is 1"을 출력

반복문과 다른 결정적인 차이는 다른 숫자를 찾기 위해 멈췄던 지점에서 다시 실행이 된다.

 

일부 요청 방식을 변경하여 자세히 확인해보자.

val seq = sequence {
    println("Generating first")
    yield(1)
    println("Generating second")
    yield(2)
    println("Generating third")
    yield(3)
    println("Done")
}

fun main() {
    val iterator = seq.iterator()
    println("Starting")
    val first = iterator.next()
    println("First : $first")
    val second = iterator.next()
    println("Second : $second")
}

 

Starting
Generating first
First : 1
Generating second
Second : 2

 

iterator는 다음 값을 얻기 위해 사용된다.

어떤 지점이든 상관없이 이터레이터를 호출하면 빌더 함수의 이전 지점으로 다시 돌아가 다음 값을 생성한다.

 

코루틴 없이 이런게 가능한지 생각해보자.

스레드가 이 일을 대신할 수도 있다, 하지만 중단을 지원하는 스레드로 처리하려면 유지하고 관리하는데 더 큰 비용이 든다.

이터레이터는 자원을 거의 사용하지 않기 때문에 계속 유지하더라도 큰 부담이 되지 않는다.

 

 

이런 예제들을 살펴보자

import java.math.BigInteger

val fibonacci: Sequence<BigInteger> = sequence {
    var first = 0.toBigInteger()
    var second = 1.toBigInteger()
    while (true) {
        yield(first)
        val temp = first
        first += second
        second = temp
    }
}

fun main() {
    println(fibonacci.take(10).toList())
}


//출력
/*
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

*/

 

사견, while(true) 조건으로 인하여 무한으로 생성되어야 할 것 같은데, 10개만 생성이 가능하다.

이 부분도 중단함수로 인한 것인가?

반응형