Go: iter 迭代器
1 · iter 迭代器#
总结: iter迭代器本质对一个序列进行迭代+操作: 如果将迭代完全放在迭代器函数内部,用户只需要提供迭代逻辑,即回调函数,这就是Push类型的迭代器,可以结合range over func使用; 反之由用户决定如何迭代(通过next函数拉值,通过stop函数停止迭代),这就是Pull类型的迭代器。
序列迭代器:定义、相关操作
go doc iter
- Iterators 迭代器:
- Naming Conventions 命名惯例:
- Single-Use Iterators 一次性迭代器:
- Pulling Values 拉值:
- Standard Library Usage 标准库的使用:
- Mutation 可变性:
1.1 · 什么是迭代器
迭代器是一个函数,其持续将某个序列中的连续元素,传递给某个回调函数(go中惯称yield)进行处理。
迭代器会在 1)序列遍历结束 2)回调函数yield返回false 的时候停止。
iter 包定义了迭代器类型iter.Seq和iter.Seq2,两者的区别是回调函数yield接受的参数个数不同。
分别对应slice包和map包?
go doc iter.Seq
# type Seq[V any] func(yield func(V) bool)
# 表示序列为单个元素
go doc iter.Seq2
# type Seq2[K, V any] func(yield func(K, V) bool)
# 表示序列为`键-值`/`索引-值`对
因为for range支持函数,可以直接在这里使用迭代器(将forrange的内部逻辑,转换成了回调函数)。
1.2 · 迭代器命令惯例
迭代器是一个函数,Go中方法也是一个函数,所以迭代器可能是一个函数,也可能是一个方法; 那迭代器函数或方法如何命名呢?
- 集合类型上的迭代器方法,通常为All
// All returns an iterator over all elements in s.
func (s *Set[V]) All() iter.Seq[V]
- 类型上有多个序列,则迭代器名表示使用的哪个序列
// Cities returns an iterator over the major cities in the country.
func (c *Country) Cities() iter.Seq[*City]
// Languages returns an iterator over the official spoken languages of the country.
func (c *Country) Languages() iter.Seq[string]
- 如果迭代有多种可能的顺序,函数名/方法名体现
// Backward returns an iterator over the list from tail to head.
func (l *List[V]) Backward() iter.Seq[V]
// Preorder returns an iterator over all nodes of the syntax tree
// beneath (and including) the specified root, in depth-first preorder,
// visiting a parent node before its children.
func Preorder(root Node) iter.Seq[Node]
1.3 · 一次性迭代器
大部分迭代器都是支持遍历整个序列,支持重复调用(每次都是遍历整个序列);
一次性迭代器无法倒带从头开始遍历(需要通过文档表示当前函数/方法为一次性迭代器):
1)提前结束后再次调用会从上次停止的位置开始,
2)序列遍历结束后再次调用不会得到任何值;
1.4 · 拉值
所有入参或出参为迭代器的函数/方法,参数类型务必使用iter.Seq/iter.Seq1,以保持range over func的兼容性(或其他迭代器适配器)
标准库的迭代器,可以看作
Push推值迭代器,其将值推送给回调函数yield有些时候,
Pull拉值的方式更符合直觉;Push是编译器帮你做好遍历(你只需要提供回调函数),Pull是用户决定什么遍历(你决定什么时候拉取值)
Pull拉值迭代器,返回两个函数:1) next拉取值 2) stop停止迭代器
iter.Pull 将Push类型迭代器转换成Pull拉值类型迭代器
go doc iter.Pull
# func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func())
go doc iter.Pull2
# func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func())
1.5 · 标准库使用
1.6 · 可变性
迭代器只提供序列的值,不提供直接修改序列的方法; 如果希望修改值,需要定义支持修改方法的类型,使用修改方法取修改值(指针接收器)。