使用 Yiedld 和 YieldAll 生成数据
使用 yield,可以通过多种方式编写生成器函数来处理无限的数据集合。
当一个序列只是生成单个数据,仅使用 yield 即可满足需求。当遇到序列时,它会挂起序列,然后继续进行下一次迭代,以此类推。
以下是 SequenceYieldExample.kt 中提供的演示功能的工作示例:
fun main() {
	// 1
	val sequence = singleValueExample()
	sequence.forEach {
		println(it)
	}
}
fun singleValueExample() = sequence {
	// 2
	println("Printing first value")
    yield("Apple")
    
	// 3
	println("Printing second value")
	yield("Orange")
    
	// 4
	println("Printing third value")
	yield("Banana")
}
上述代码片段有几件事情:
- 你使用 
singleValueExample()创建了一个序列并迭代它打印每个子项。 - 在 
singleValueExample()中你首先打印了一条语句,然后yield("Apple")。 - 加下来你同样生成了第二个数据 —— “Orange”。
 - 最后是第三个数据 —— “Banana”。
 
执行上述代码,输出如下:
Printing first value
Apple
Printing second value
Orange
Printing third value
Banana
上述代码片段一次打印一个数据,然后挂起指定遇到下一个 yield 才会恢复。这是一个非常简单的过程,其中数据是一个接一个地生成的,yield 确保一次处理一个数据。实际上,可以通过 yield 不断产生更多的数据。
然而,这就引出了一个问题,如何使用一个范围数据生成序列?你应该不希望每当范围数据的序列中生成新值时都调用 yield 函数。是时候使用 yieldAll(elements:Iterable<T>) 函数了。此函数的签名如下:
public suspend fun yieldAll(elements: Iterable<T>)
为了验证它的行为,以下是 IteratorYieldAllExample.kt 中提供的功能演示代码片段:
fun main() {
	// 1
	val sequence = iterableExample()
	sequence.forEach {
		print("$it ")
	}
}
fun iterableExample() = sequence {
	// 2
	yieldAll(1..5)
}
在上述代码中:
- 你使用 
iterableExample()创建了一个序列并迭代它打印每个子项。 - 你通过 
yieldAll生成了 [1..5] 一组整数,当每次生成一个整数时都会挂起。 
执行上述代码片段,输出如下:
1 2 3 4 5
在序列是无限的时,Kotlin 标准库提供了另一个辅助函数,它是一个名为 yieldAll(sequence: Sequence<T>) 的重载函数,它将序列作为入参而不是迭代器。
下面是它在源码中的声明:
public suspend fun yieldAll(sequence: Sequence<T>) = yieldAll(sequence.iterator())
要演示其用法,请查看以下示例,该示例在 SequenceYieldAllExample.kt 中提供:
fun main() {
	// 1
	val sequence = sequenceExample().take(10)
	sequence.forEach {
		print("$it ")
	}
}
fun sequenceExample() = sequence {
	// 2
	yieldAll(generateSequence(2) { it * 2 })
}
同样的,在上述代码中:
- 使用 
sequenceExample()创建了一个序列,然后迭代它打印每个子项。 - 使用 
generateSequence()生成一个无限的整数数字并传递每次生成的整数给yieldAll函数。生成整数的方式是从数字 2 开始,然后将其加倍。 
一旦执行上述代码片段,输出如下:
2 4 8 16 32 64 128 256 512 1024
sequenceExample() 生成了一个无限的序列,但为了保证程序可用,通过对生成无限序列的 sequenceExample() 调用 take(10),该序列被限制为序列中的前 10 项。
每次生成整数时,sequenceExample 下的代码块都会挂起。这就是为什么你可以在 main 中使用 forEach 打印数据,然后返回并生成一个新的数据,然后再次打印。以此类推,或者至少直到内存用完或者使用 take 到达最后一个请求的数据。
