데이터소스에서 정보를 검색하고 표시할 때 온 디맨더 방식을 사용하는 것이 일반적
ex) 뉴스 리더에서 사용자가 처음 앱을 열 때 일부 기사만 가져오고 아래로 스크롤할 때 데이터를 더 가져온다.
지금까지 하나 또는 그 이상의 연산이 실행되기를 대기하는 동안 일시 중단하는 함수만을 알아보고 구현했음
호출 사이에서 일시 중단되지만, 실행 중에는 일시 중단될 수 없음
-> 일시 중단 연산이 없어도 반복할 수 있음
시퀀스와 이터레이터의 빌더는 CoroutineContext를 받지 않음
기본적으로 코드를 호출한 컨텍스트와 동일한 컨텍스트에서 코드가 실행됨을 의미
CoroutineContext :
Coroutine이 실행되는 환경이라고 생각하면 된다.
Dispatcher와 CoroutineExceptuonHandler 또한 Coroutine이 실행되는 환경의 일부이며,
이 둘 모두는 CoroutineContext에 포함되어 Coroutine이 실행되는 환경으로 볼 수 있다.
값을 산출하면 값이 다시 요청될 때까지 시퀀스 또는 이터레이터가 일시 중단 됨
ex)
val iterator = iterator { yield("First") yield("Second") yield("Third") } println(iterator.next()) println(iterator.next()) println(iterator.next()) /* 원서에서는 buildIterator를 이용해서 iterator를 생성하지만, 코틀린 1.3 버전 부터는 kotlin.sequences.iterator를 사용하도록 변경됐다. */
↪ 이 코드는 세 가지 요소를 포함하는 이터레이터를 빌드
요소가 처음 요청될 때 첫 번째 줄이 실행되고 "First"값이 산출 이후 실행 중단
세 가지 요소를 얻으려면 next() 함수를 세 번 호출
실행 중에 일시 중단할 수 없으므로 일시 중단 시퀀스/이터레이터는
일시 중단 불가능한 코드에서 호출할 수 있다. -> runBlocking ❌❌
인덱스 요소를 검색할 수 없으므로 요소는 순서대로 액세스할 수 있음
더 많은 요소가 있는지 여부를 나타내는 hasNext() 함수 존재
요소는 한 방향으로만 검색, 이전 요소 검색할 방법 ❌
재설정할 수 없으므로 한 번만 반복할 수 있음 ✔️
일시 중단 이터레이터를 작성하기 위해 iterator()를 사용해 이터레이터 본문과 함께 람다를 전달
달리 지정되지 않는 한 Iterator<T> 를 리턴
-> T는 이터레이터가 생성하는 요소에 의해 결정
-> 어떤 이유든 재정의하려면 타입을 정의할 수 있음
// 컴파일 오류 X val iterator : Iterator<Any> = iterator { yield(1) yield(10L) yield("Hello") } ----------------------------------------------- // 컴파일 오류 발생 val iterator : Iterator<String> = iterator { yield("Hello") yield(1) }
이터레이터의 모든 요소를 하나씨 가져오는 대신 한꺼번에 가져오는 경우도 존재
전체 이터레이터를 반복하기 위해 forEach() / forEachReamining() 함수를 사용할 수 있음
-> 이터레이터는 한 방향으로만 갈 수 있기 때문에 두 기능 모두 똑같이 동작
iterator.forEach{ println(it) }
⚡
두 함수는 모두 같은 방식으로 작동
일부 요소를 이미 읽었다면 forEachRemaining()을 사용하면 코드를 읽는 사람에게 해당 시점까지 일부 요소가 이터레이터에 없을 수 있음을 명확히 안다.
-> ??
fun main(args: Array<String>) { val iterator = iterator { for(i in 0..4) yield(i * 4) } for(i in 0..5) if(iterator.hasNext()) println("Element $i is ${iterator.next()}") else println("No more Elements") }
val iterator2 = iterator { yield(1) } println(iterator2.next()) println(iterator2.next())
hasNext() 가 작동하려면 런타임은 코루틴 실행을 재개함
-> 새로운 값이 나오면 true를 반환,
더 이상 값이 없어 이터레이터 실행이 끝나면 false를 반환
hasNext() 호출로 인해 값이 산출되면 값이 유지되거나 다음에 next() 를 호출할 때 값이 반환
val iterator = iterator { println("yielding 1") yield(1) println("yielding 2") yield(2) } iterator.next() if(iterator.hasNext()) { println("iterator has next") iterator.next() }
인덱스로 값을 가져올 수 있음
상태가 저장되지 않으며, 상호 작용한 후 자동으로 재설정 됨
한 번의 호출로 값 그룹을 가져올 수 있음
val sequence = sequence { yield(1) }
val sequence: Sequence<Any> = sequence { yield("A") yield(1) yield(32L)
sequence.forEach { print("$it") } sequence.forEachIndexed { index, value -> println("element at $index is $value") }
ElementAt()
함수는 다음과 같이 인덱스를 가져와 해당 위치의 요소를 반환
sequence.elementAt(2)
elementAtOrElse()
함수는 주어진 인덱스에 요소가 없으면 람다로 실행
-> 람다는 전달된 인덱스를 받음
sequence.elementAtOrElse(10,{it*2})
-> 시퀀스에 10개의 요소가 안되면 인덱스 10에 2를 곱한 20을 반환
elementAtOrNull()
함수는 인덱스를 가져와서 T?를 반환
-> 주어진 인덱스에 요소가 없으면 null을 반환
sequence.elementAtOrNull(10)
요소 그룹 얻기
값을 그룹으로 가져올 수 있음
// sequence = (1,1,2,3,5,6,13,21) 이 들어있는 상황 val firstFive = sequence.take(5) println(firstFive.joinToString()) // 1, 1, 2, 3, 5 // 쉼표로 구분된 처음 5 개의 값이 출력
⚡
take()는 중간 연산이므로 나중에 종단 연산이 호출되는 시점에 계산돼
Sequence<T>를 반환한다는 점을 주목
-> 실제로 시퀀스에는 값을 갖지 않지만 joinToString()을 호출하면 값을 갖게 됨
val sequence = sequence { for(i in 0..9){ println("Yielding $i") yield(i) } } println("Requesting index 1") sequence.elementAt(1) println("Requesting index 2") sequence.elementAt(5) println("Taking 3") sequence.take(3).joinToString()
↪ 이터레이터를 사용하는 것과 달리 시퀀스는 각각의 호출마다 요소의 처음부터 실행됨
-> 질문
elementAt은 인덱스를 가져와서 해당 위치의 요소를 반환하는게 아닌가?
-> 해당 위치의 인덱스까지 반환하는 이유가 무엇인지
- CoroutineContext에 대해
↪ https://kotlinworld.com/152