일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- python
- 플러터
- 콜렉션
- textstyle
- package
- animation
- Flutter
- Yocto
- Android
- 다트
- variable
- pushnamed
- Class
- List
- DART
- 크롤러
- 클래스
- Collection
- set
- 웹크롤러
- ML
- map
- crawler
- kotlin
- function
- 코틀린
- 파이썬
- 함수
- import
- text
- Today
- Total
조용한 담장
코틀린(Kotlin) Collections : 시퀀스(sequences) 본문
코틀린(kotlin) 의 시퀀스(sequence) 를 살펴보자.
원문 https://kotlinlang.org/docs/reference/sequences.html 을 보며 정리.
kotlin standard library 는 collection 과 함께 또다른 container type 인 sequences (Sequence<T>) 를 가지고 있다.
여러 단계를 포함하는 Iterable 을 수행할 때에는 각 단계의 처리는 바로 끝나고 즉각 그 결과를 리턴한다.
sequences 의 여러 단계 처리는 전체 단계가 처리된 결과가 요구됬을 때에 실제 연산이 일어나며 느리게(나중에) 처리된다. (executed lazily)
동작 수행의 순서 또한 다르다. Sequence 은 각각 하나의 element 에 대해 모든 단계를 수행한다.
Iterable 은 전체 collection 에 대해 각 단계의 수행을 완료하고 다음 단계로 넘어간다.
따라서, sequence 는 중간 단계의 결과에 대한 처리를 피할 수 있게 해주며, collection 전체 처리에 대한 수행 성능이 향상된다.
하지만 크기가 작은 collection 이나 단순한 연산 동작에 대해서는 오히려 불필요한 오버헤드가 생길 수 있다.
그러므로 어느 경우에 Sequence 나 Iterable 이 나을지 적절한 선택을 해야 한다.
Constructing
From elements
sequence 를 생성하려면 argument 로 element 를 나열하는 sequenceOf() 함수를 호출한다.
val numbersSequence = sequenceOf("four", "three", "two", "one")
From Iterable
List, Set 같은 Iterable object 를 이미 가지고 있다면 asSequence() 함수를 통해 sequence 를 생성할 수 있다.
val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()
From function
element 를 생성하는 함수를 argument 로 사용해서 sequence 를 생성하는 방법으로 generateSequence() 함수를 사용한다.
첫번째 element 를 특정 값으로 지정 하거나 함수 호출의 결과를 지정하는 것도 가능하다.
함수가 null 을 리턴하면 sequence 생성은 멈추게 된다.
아래의 코드는 무한히 생성되는 오류를 가진다.
val oddNumbers = generateSequence(1) { it + 2 } // `it` is the previous element
println(oddNumbers.take(5).toList()) // [1, 3, 5, 7, 9]
//println(oddNumbers.count()) // error: the sequence is infinite
generateSequence() 함수로 유한한 sequence 를 만드려면 마지막 element 뒤에 null 을 리턴하는 함수를 제공해야 한다.
val oddNumbersLessThan10 = generateSequence(1) { if (it < 10) it + 2 else null }
println(oddNumbersLessThan10.count()) // 6
From chunks
sequence() 함수를 사용하여 임의의 크기의 chunk 의 각각의 element 로 sequence 를 생성할 수 있다.
이 함수는 yield() 와 yieldAll() 함수 호출을 포함하는 람다 표현식을 가진다.
두 함수는 element 를 sequence consumer 에게 리턴하며 다음 consumer 의 다음 element 에 대한 요청이 있을 때까지 sequence() 의 실행은 중단된다.
yield() 는 한개의 element 를 argument 로 가지고, yieldAll() 는 Iterable object, Iterator 또는 다른 Sequence 를 가진다.
yieldAll() 의 argument 가 되는 Sequence 는 무한할 수 있으나 이때의 호출은 항상 마지막이 되어야 하며 그렇지 않으면 그 뒤의 정의된 argument 들은 호출되지 않는다.
val oddNumbers = sequence {
yield(1)
yieldAll(listOf(3, 5))
yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList())
// output:
// [1, 3, 5, 7, 9]
Sequence operations
sequence 수행 동작은 state 요구사항에 따라 아래처럼 나뉠 수 있다.
- Stateless 수행 동작은 state 를 요구하지 않고 element 각각을 독립적으로 처리한다. 예를들어, map() 이나 filter() 이 있다. Stateless 수행 동작은 또한 element 를 처리하기 위한 작은 일정 양의 state 를 요구할 수 있다. 예를들어, take(), drop() 이 있다.
- Stateful 수행 동작은 보통 sequence 의 element 개수에 비례하여 많은 양의 state 를 요구한다.
sequence 수행 동작이 늦게 생성되는(produced lazily) 다른 sequence 를 리턴 한다면 그것은 intermediate 라 하고 다른 동작은 terminal 이라 한다.
terminal 수행 동작의 예로는 toList() 나 Sum() 이 있다.
Sequence element 는 오직 terminal 수행 동작으로 부터만 얻어질 수 있다.
sequence 는 여러번의 iteration 동작이 가능하나 어떤 sequence 구현은 단 한번으로 제한될 수 있는데 이는 해당되는 것들에 문서화 되어 있다.
Sequence processing example
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)
// output:
// 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() 에서 모든 element 에 대해 동작이 수행되고 나서 map() 에서 filter() 의 결과로 리턴된 element 에 대해 수행된다.
Sequence
val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a 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")
// terminal operation: obtaining the result as a List
println(lengthsSequence.toList())
// output:
// 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() 이 실제 필요로 하는 때에 호출되는 것을 볼 수 있다.
또한 map() 은 filter() 가 element 를 리턴하자 마자 바로 실행되는 것을 볼 수 있다.
그리고 take() 에 의해 최종 결과의 개수가 4개를 만족하면 나머지 수행은 멈추고 모든 동작이 마무리 된다.
위 예제를 보면 같은 크기의 list 에 대해 iterable 은 23 단계를 수행하지만 sequence 는 18 단계만 수행을 하는 것을 알 수 있다.
'kotlin' 카테고리의 다른 글
코틀린(Kotlin) Collections : Transformations (0) | 2020.03.18 |
---|---|
코틀린(Kotlin) Collections : Operations Overview (0) | 2020.01.02 |
코틀린(Kotlin) Collections : Ranges and Progressions (0) | 2020.01.02 |
코틀린(Kotlin) Collections : Iterators (0) | 2020.01.02 |
코틀린(Kotlin) Collections : Constructing Collections (0) | 2020.01.02 |