본문 바로가기
Kotlin

[Kotlin] Kotlin 공식 문서 번역 - 시퀀스 (Sequence)

by 노력남자 2023. 9. 26.
반응형

시퀀스 (Sequence)

 

컬렉션과 함께 Kotlin 표준 라이브러리에는 또 다른 유형인 시퀀스(Sequence<T>)가 포함되어 있습니다. 컬렉션과는 달리 시퀀스는 요소를 포함하지 않고 반복 중에 요소를 생성합니다. 시퀀스는 Iterable과 동일한 함수를 제공하지만 다단계 컬렉션 처리에 대한 다른 접근 방식을 구현합니다.

Iterable의 처리가 여러 단계를 포함할 때, 이러한 단계는 즉시 실행됩니다. 각 처리 단계가 완료되고 결과인 중간 컬렉션을 반환합니다. 그 다음 단계는 이 컬렉션에서 실행됩니다. 반면에 시퀀스의 다단계 처리는 가능한 경우 게으르게 실행됩니다. 실제 계산은 전체 처리 체인의 결과가 요청될 때만 발생합니다.

작업 실행 순서도 다릅니다. 시퀀스는 모든 처리 단계를 각 요소마다 하나씩 수행합니다. Iterable은 각 단계를 전체 컬렉션에 대해 완료한 다음 다음 단계로 진행합니다.

따라서 시퀀스를 사용하면 중간 단계의 결과물을 만들지 않고 전체 컬렉션 처리 체인의 성능을 향상시킬 수 있습니다. 그러나 시퀀스의 게으른 특성은 더 작은 컬렉션을 처리하거나 간단한 계산을 수행할 때 중요할 수 있는 몇 가지 오버헤드를 추가합니다. 따라서 Sequence와 Iterable을 모두 고려하고 경우에 따라 어떤 것이 더 적합한지 결정해야 합니다.


구성

 

요소로부터


시퀀스를 만들려면 sequenceOf() 함수를 호출하고 인수로 요소를 나열하십시오.

 

val numbersSequence = sequenceOf("four", "three", "two", "one")


Iterable에서


이미 Iterable 객체(예: List 또는 Set)를 가지고 있다면 asSequence()를 호출하여 시퀀스로 만들 수 있습니다.

 

val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()


함수에서


시퀀스를 만드는 또 다른 방법은 요소를 계산하는 함수로 구축하는 것입니다. 함수를 기반으로 시퀀스를 구축하려면 이 함수를 인수로하여 generateSequence()를 호출하십시오. 옵션으로 첫 번째 요소를 명시적 값 또는 함수 호출 결과로 지정할 수 있습니다. 제공된 함수가 null을 반환할 때 시퀀스 생성이 중단됩니다. 따라서 아래 예에서 시퀀스는 무한합니다.

 

val oddNumbers = generateSequence(1) { it + 2 } // `it`은 이전 요소입니다.
println(oddNumbers.take(5).toList())


generateSequence()로 유한 시퀀스를 만들려면 필요한 마지막 요소 이후에 null을 반환하는 함수를 제공하십시오.

 

val oddNumbersLessThan10 = generateSequence(1) { if (it < 8) it + 2 else null }
println(oddNumbersLessThan10.count())


청크에서


마지막으로, 요소를 하나씩 또는 임의의 크기의 청크로 생성할 수 있는 함수가 있습니다 - sequence() 함수입니다. 이 함수는 yield() 및 yieldAll() 함수를 호출하는 lambda 식을 포함합니다. 이들 함수는 요소를 시퀀스 소비자에게 반환하고 요소가 소비자에 의해 요청될 때까지 sequence()의 실행을 일시 중단합니다. yield()는 단일 요소를 인수로 취하며 yieldAll()은 Iterable 객체, Iterator 또는 다른 Sequence를 취할 수 있습니다. yieldAll()의 Sequence 인수는 무한할 수 있습니다. 그러나 이러한 호출은 마지막 호출이어야 합니다. 그 후의 모든 호출은 실행되지 않습니다.

val oddNumbers = sequence {
    yield(1)
    yieldAll(listOf(3, 5))
    yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList())

 

시퀀스 작업


시퀀스 작업은 상태 요구 사항에 따라 다음 그룹으로 분류할 수 있습니다.

  • 상태가 없는 작업은 상태를 필요로하지 않으며 각 요소를 독립적으로 처리합니다. 예를 들어 map() 또는 filter()가 있습니다. 또한 상태가 없는 작업은 요소를 처리하는 데 작은 고정 된 양의 상태가 필요할 수 있습니다. 예를 들어 take() 또는 drop()가 있습니다.
  • 상태가 있는 작업은 일반적으로 시퀀스의 요소 수에 비례하는 상당한 양의 상태가 필요합니다.

 

시퀀스 작업이 게으르게 생성되는 다른 시퀀스를 반환하는 경우 중간 작업이라고 합니다. 그렇지 않으면 작업은 종단 작업입니다. 종단 작업의 예는 toList() 또는 sum()입니다. 시퀀스 요소는 종단 작업만을 사용하여 검색할 수 있습니다.

시퀀스는 여러 번 반복될 수 있지만 일부 시퀀스 구현은 한 번만 반복될 수 있도록 제한할 수 있습니다. 이러한 내용은 문서에 명시적으로 언급됩니다.

 

시퀀스 처리 예시


Iterable과 Sequence의 차이를 예제를 통해 살펴보겠습니다.


Iterable


단어 목록이 있다고 가정해보겠습니다. 아래 코드는 세 글자보다 긴 단어를 필터링하고 그 중 첫 네 개의 단어의 길이를 출력합니다.

 

val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)
​
println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)

// 결과
/*
filter: The
filter: quick
filter: brown
filter: fox
filter: jumps
filter: over
filter: the
filter: lazy
filter: dog
length: 5
length: 5
length: 5
length: 4
length: 4
Lengths of first 4 words longer than 3 chars:
[5, 5, 5, 4]
*/


이 코드를 실행하면 filter() 및 map() 함수가 코드에 나타나는 순서대로 실행됨을 볼 수 있습니다. 먼저 모든 요소에 대한 "filter:"가 나타나고, 필터링 후 남은 요소에 대한 "length:"가 나타나며, 그런 다음 두 개의 마지막 줄의 출력이 표시됩니다.

리스트 처리 과정은 다음과 같습니다:

 


Sequence


이제 시퀀스로 동일한 작업을 수행해 보겠습니다:

val words = "The quick brown fox jumps over the lazy dog".split(" ")
// List를 Sequence로 변환
val wordsSequence = words.asSequence()
​
val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)
​
println("Lengths of first 4 words longer than 3 chars")
// 종단 작업: 결과를 List로 얻기
println(lengthsSequence.toList())

// 결과
/*
Lengths of first 4 words longer than 3 chars
filter: The
filter: quick
length: 5
filter: brown
length: 5
filter: fox
filter: jumps
length: 5
filter: over
length: 4
[5, 5, 5, 4]
*/


이 코드의 출력 결과는 결과 목록을 구축할 때만 filter() 및 map() 함수가 호출됨을 보여줍니다. 따라서 먼저 "Lengths of.." 텍스트 줄을 보고 시퀀스 처리가 시작됩니다. 필터링 후 남은 요소에 대해서는 맵이 다음 요소를 필터링하기 전에 실행됩니다. 결과 크기가 4에 도달하면 처리가 중지됩니다. take(4)가 반환할 수 있는 가장 큰 크기이기 때문입니다.

시퀀스 처리는 다음과 같이 진행됩니다:

 


이 예제에서는 시퀀스 처리가 동일한 작업을 수행하는 리스트에 비해 23단계 대신 18단계가 소요된다는 것을 보여줍니다.

 

원문

 

https://kotlinlang.org/docs/sequences.html

반응형

댓글