Background

Discussion-54245: standard iterator interface

关于 Iterator 的提案,包括为什么现在提议这个,以及希望提供的功能。

Discussion-56413: user-defined iteration using range over func values

关于 for range 和 Push/Pull 函数相关 (这个讨论该看)

Discussion-56010: redefining for loop variable semantics

Loop 变量作用域,1.22 已经包含了

Discussion-43557: function values as iterators
Discussion-43557-comment

这两个更早点。也是 Iterator 相关。

Issue-61897: Iter pakcage

关于 1.23 新增的 iter 包及其相关功能。

Simple Summary

大多数语言提供了一种标准化方法来使用迭代器接口迭代存储在容器中的值。
Go 提供了与 map、slice、string、array 和 channel 一起使用的 for range ,但它没有为用户编写的容器提供任何通用机制,也没有提供迭代器接口。
这导致 Go 相关的非泛型迭代器的用法五花八门:

部分原因是在引入泛型之前,无法编写描述迭代器的接口。

不过现在有泛型了,我们可以为具有 E 类型元素的容器上的迭代器编写一个接口 Iter[E]
其他语言中迭代器的存在表明这是一个强大的工具。

Push/Pull functions

#56413 讨论中关于 push/pull 函数的功能讨论的很多,包括它们的互相转换。
不过和 iter 包现有的并不完全一样,大体概念倒是没变。

Iter Package

目前来说做的不多,两个类型和两个函数签名

1
2
3
4
5
type Seq[V any] func(yield func(V) bool)
type Seq2[K, V any] func(yield func(K, V) bool)

func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func())
func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func())

slices 和 maps 包中也添加了相关的一些函数,这里随便挑一个看下

1
2
3
4
5
6
7
8
9
10
11
// All returns an iterator over index-value pairs in the slice
// in the usual order.
func All[Slice ~[]E, E any](s Slice) iter.Seq2[int, E] {
return func(yield func(int, E) bool) {
for i, v := range s {
if !yield(i, v) {
return
}
}
}
}

用法大概就是:

1
2
3
4
5
6
7
8
nums := []int{2,3,4,5}
for _, v := range slices.All(nums) {
fmt.Println(v)
}
// 2
// 3
// 4
// 5

尝试写一个吧,比如获取一个 slice 中的奇数:

1
2
3
4
5
6
7
8
9
10
11
func Odd[Slice ~[]E, E int](s Slice) iter.Seq2[int, E] {
return func(yield func(int, E) bool) {
for i, v := range s {
if v % 2 != 0 {
if !yield(i, v) {
return
}
}
}
}
}

这个例子不算太好,因为 v % 2 != 0 这一步约束了这个类型必须是整数,这里我就简单的标注为 E int 了。

现在就可以用了:

1
2
3
4
5
6
nums := []int{2,3,4,5,6}
for _, v := range Odd(nums) {
fmt.Println(v)
}
// 3
// 5

但这么写是有点奇怪的,挺奇怪的,我们可以直接操作 yield 函数

1
2
3
4
5
6
7
8
slices.All(nums)(func (k, v int) bool {
if v % 2 != 0 {
fmt.Println(v)
}
return true
})
// 3
// 5

然后是 PULL 函数

比如说写一个交换 map k,v 对的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func ReplaceKV[K comparable](seq iter.Seq2[K, K]) iter.Seq2[K, K] {
return func(yield func(K, K) bool) {
next, stop := iter.Pull2(seq)
defer stop()
for {
k, v, ok := next()
if !ok {
return
}
if !yield(v, k) {
return
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
m := map[string]string{"cn": "CHINA", "en": "ENGLISH", "us":"AMERICA"}
for k, v := range maps.All(m) {
fmt.Println(k, ": ", v)
}
fmt.Println("---")
for k, v := range ReplaceKV(maps.All(m)) {
fmt.Println(k, ": ", v)
}
// us : AMERICA
// cn : CHINA
// en : ENGLISH
// ---
// ENGLISH : en
// AMERICA : us
// CHINA : cn

这里是只用了 slice 和 map 举例,所以更像个语法糖。
应该和结构体或者说复合类型结合起来看,效果会更好,我就不继续写了。

而且这确实是规范了 Iterator 的写法。