破解 Kotlin 协程(7) - 序列生成器篇

1. 认识 Sequence

`````` val fibonacci = sequence {
yield(1L) // first Fibonacci number
var cur = 1L
var next = 1L
while (true) {
yield(next) // next Fibonacci number
val tmp = cur + next
cur = next
next = tmp
}
}

fibonacci.take(5).forEach(::log)

``````10:44:34:071 [main] 1
10:44:34:071 [main] 1
10:44:34:071 [main] 2
10:44:34:071 [main] 3
10:44:34:071 [main] 5

``````val seq = sequence {
log("yield 1,2,3")
yieldAll(listOf(1, 2, 3))
log("yield 4,5,6")
yieldAll(listOf(4, 5, 6))
log("yield 7,8,9")
yieldAll(listOf(7, 8, 9))
}

seq.take(5).forEach(::log)

``````10:44:34:029 [main] yield 1,2,3
10:44:34:060 [main] 1
10:44:34:060 [main] 2
10:44:34:060 [main] 3
10:44:34:061 [main] yield 4,5,6
10:44:34:061 [main] 4
10:44:34:066 [main] 5

2. 深入序列生成器

``````override suspend fun yield(value: T) {
nextValue = value
return suspendCoroutineUninterceptedOrReturn { c ->
nextStep = c
COROUTINE_SUSPENDED
}
}

``````override suspend fun yieldAll(iterator: Iterator<T>) {
if (!iterator.hasNext()) return
nextIterator = iterator
return suspendCoroutineUninterceptedOrReturn { c ->
nextStep = c
COROUTINE_SUSPENDED
}
}

``````override fun next(): T {
when (state) {
return nextIterator!!.next()
}
val result = nextValue as T
nextValue = null
return result
}
else -> throw exceptionalState()
}
}

• ① 是下一个元素还没有准备好的情况，调用 `nextNotReady` 会首先调用 `hasNext` 检查是否有下一个元素，检查的过程其实就是调用 `Continuation.resume`，如果有元素，就会再次调用 `next`，否则就抛异常
• ② 表示我们调用了 `yieldAll`，一下子传入了很多元素，目前还没有读取完，因此需要继续从传入的这个元素集合当中去迭代
• ③ 表示我们调用了一次 `yield`，而这个元素的值就存在 `nextValue` 当中

`hasNext` 的实现也不是很复杂：

``````override fun hasNext(): Boolean {
while (true) {
when (state) {
if (nextIterator!!.hasNext()) {
return true
} else {
nextIterator = null
}
State_Done -> return false // ③
else -> throw exceptionalState()
}

state = State_Failed
val step = nextStep!!
nextStep = null
step.resume(Unit)
}
}

CSDN：Kotlin中文社区