懶序列是啥?
不使用就不初始化,調(diào)用時(shí)才加載的序列就是懶序列。kotlin中,可以利用協(xié)程的掛起和恢復(fù)特性,實(shí)現(xiàn)懶序列。
期望達(dá)到的效果
fun main() {
val arr = generator<Int> {
for (i in 0..5) {
println("$i 準(zhǔn)備好啦。")
yield(i)
}
}
for (i in arr) {
println("輸出 $i")
}
}
// 控制臺(tái)輸出
0 準(zhǔn)備好啦。
輸出 0
1 準(zhǔn)備好啦。
輸出 1
2 準(zhǔn)備好啦。
輸出 2
3 準(zhǔn)備好啦。
輸出 3
4 準(zhǔn)備好啦。
輸出 4
5 準(zhǔn)備好啦。
輸出 5
Process finished with exit code 0
難點(diǎn)分析
- 實(shí)現(xiàn)一個(gè)generator方法,接受一個(gè)函數(shù),并返回一個(gè)序列
- 限定yield方法只能在generator方法的lambda表達(dá)式的作用域中使用
- generator方法接受的函數(shù)必須是掛起函數(shù),否則無(wú)法達(dá)到懶加載的目的
- 為了支持 foreach 表達(dá)式,需要讓generator方法返回一個(gè)Iterator對(duì)象
具體實(shí)現(xiàn)
import kotlin.coroutines.*
fun main() {
val arr = generator<Int> {
for (i in 0..5) {
println("$i 準(zhǔn)備好啦。")
yield(i)
}
}
for (i in arr) {
println("輸出 $i")
}
}
fun <T> generator(block: suspend GeneratorScope<T>.() -> Unit): Iterator<T> {
return GeneratorIterator(block)
}
sealed class State {
class NotReady(val continuation: Continuation<Unit>) : State()
class Ready<T>(val continuation: Continuation<Unit>, val nextValue: T) : State()
object Done : State()
}
class GeneratorIterator<T>(block: suspend GeneratorScope<T>.() -> Unit) :
Iterator<T>, GeneratorScope<T>, Continuation<Unit> {
override val context: CoroutineContext = EmptyCoroutineContext
private var state: State
init {
val startContinuation = block.createCoroutine(this, this)
state = State.NotReady(startContinuation)
}
/**
* yield方法干了啥
* 1.掛起當(dāng)前方法,
* 2.恢復(fù)遍歷處的方法,并將值通過(guò)狀態(tài)傳給遍歷處
* 3.流轉(zhuǎn)狀態(tài),將狀態(tài)從 NotReady 改成 Ready
*/
override suspend fun yield(value: T) = suspendCoroutine<Unit> {
state = State.Ready(it, value)
}
/**
* hasNext方法干了啥
* 1.如果當(dāng)前狀態(tài)為 NotReady, 則恢復(fù)NotReady協(xié)程讓狀態(tài)進(jìn)行到 Ready 或者 Done
* 2.然后判斷當(dāng)前state是否是Done,來(lái)確定是否還有下一個(gè)元素
*/
override fun hasNext(): Boolean {
if (state is State.NotReady) {
(state as State.NotReady).continuation.resume(Unit)
}
return state != State.Done
}
/**
* next方法干了啥
* 1.判斷是否有下一個(gè),如果有,則取出來(lái)返回,同時(shí)流轉(zhuǎn)狀態(tài),Ready -> NotReady
* 2.沒(méi)有就報(bào)數(shù)組越界
*/
override fun next(): T {
if (hasNext()) {
val currentState = state as State.Ready<T>
state = State.NotReady(currentState.continuation)
return currentState.nextValue
}
throw IndexOutOfBoundsException("No value left.")
}
override fun resumeWith(result: Result<Unit>) {
state = State.Done
}
}
/**
* 限制yield方法只能在特定的lambda表達(dá)式中使用
*/
interface GeneratorScope<T> {
suspend fun yield(value: T)
}