시퀀스 빌더와 시퀀스가 작동하기 위해 왜 중단이 필요한지 알아보자
파이썬이나 자바스크립트 등에서는 제한된 형태의 코루틴을 사용
코틀린에서는 제너레이터 대신 시퀀스 빌더를 제공하며, 시퀀스는 필요할 때마다 값을 하나씩 계산하는 지연 처리가 가능
// 자연수의 무한 시퀀스
val naturalNumbers = sequence {
var n = 1
while (true) {
yield(n++)
}
}
// 피보나치 수열의 무한 시퀀스
val fibonacci = sequence {
var terms = Pair(0, 1)
while (true) {
yield(terms.first)
terms = Pair(terms.second, terms.first + terms.second)
}
}
이러한 무한 시퀀스는 실제로 모든 값을 한 번에 계산하지 않고, take()나 first() 등의 연산자를 통해 필요한 만큼만 값을 계산합니다. 이것이 시퀀스의 지연 처리 특성입니다.
위와 같은 특징으로 값을 순차적으로 계산하여 필요할 때 반환하는 빌더를 정의하는 것이 좋음
시퀀스는 sequence 함수를 이용하며 람다식 내부에서 yield 함수를 호출하여 다음 값을 생성
val seq = sequence {
yield(1)
yield(2)
yield(3)
}
fun main() {
for (num in seq) {
print(num)
} // 123
}
🔄 시퀀스의 실행 흐름
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 $num")
}
}
// Generating first
// The next number is 1
// Generating second
// The next number is 2
// Generating third
// The next number is 3
// Done
seq.next() 를 호출하며, yield 로 값을 반환할때까지 실행 🏃♂️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()
pritnln("First: $first")
val second = iterator.next()
println("Second: $second")
// ...
}
// Starting
// Generating first
// The next number is 1
// Generating second
// The next number is 2
피보나치 수열과 같은 수학적 시퀀스 🔢
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() {
print(fibonacci.take(10).toList())
}
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]