大家好,我是煎鱼。
Go1.23 新版本中,在发布过程中争议最大的新特性莫过于:迭代器(iterators)。
原本计划先写一个这个 proposal 的提出背景的,但没想到,迭代器涉及的到 proposal 比较多,而且是由 rsc 亲自负责。
总感觉 rsc 早有预谋,在 Go1.23 蓄力一击,搞完就撤了。
我能翻到的最早明确提出要加迭代器是在 discussions/54245[1] 中进行了广泛讨论:
图片
随后折腾了许久,最终 rsc 牵头在 discussions/56413[2] 做了初步敲定:
图片
后面今年 《spec: add range over int, range over func》[3],包含在 for-range int 和 function 中再次冲击新特性:
图片
我就不一一列举和解释了。大家可以理解为比较折腾高密度讲了很久。
根据 Go 官方几个 issues 和 discussions 的说法,汇总一下。具体缘由如下:
有同学会疑惑第一点中提到的容器是什么?
实际上指代的是:使用迭代器 “提供一种按顺序访问聚合对象元素的方法,而无需暴露其底层表现”。
这句话中所说的聚合对象就是上文中所提到的容器。聚合对象或容器只是一个包含其他值的值。
具体 Go 标准库中各自为政的。例如:
有兴趣的可以自己看一下函数调用或实现。
平时写业务代码都会接触到。这里就不深入展开了。
在 Go 1.23 中,将会同时支持用户定义容器类型的 for-range 和标准化形式的迭代器。
本次新版本中:
后续通过新增的迭代器的标准定义,我们编写的函数可以顺利地与不同的容器类型配合使用。
有种可以循环遍历万物的感觉。
以下是 Go1.23 中迭代器的一些基础的标准例子。
分别包含:单值迭代器和二值迭代器。
在 Go 中,yield 关键字的引入使得函数可以像迭代器一样工作。这一特性是在 Go 1.22 版本中被提出的,允许函数在执行过程中暂时挂起,并返回一个或多个值。
这种机制与其他编程语言(如:Python)中的 yield 关键字有些相似,但在 Go 中实现的方式有所不同。
以下是关于 Go 中 yield 关键字的一些关键点:
示例代码如下:
import ( "fmt" "iter")func Stat(v int) iter.Seq[int] { return func(yield func(int) bool) { for i := v; i >= 0; i-- { if !yield(i) { return } } }}func main() { for v := range Stat(11) { fmt.Println(v) }}
输出结果:
11109876543210
示例代码如下:
func Backward[E any](s []E "E any") iter.Seq2[int, E] { return func(yield func(int, E) bool) { for i := len(s) - 1; i >= 0; i-- { if !yield(i, s[i]) { return } } }}func main() { sl := []string{"脑子", "进", "煎鱼", "了"} for i, s := range Backward(sl) { fmt.Printf("%d: %s/n", i, s) }}
输出结果:
3: 了2: 煎鱼1: 进0: 脑子
本次 Go1.23 在 slices 标准库中针对迭代器,新增了:slices.All、slices.Values、slices.Collect 方法。
函数签名如下:
func All[Slice ~[]E, E any](s Slice "Slice ~[]E, E any") iter.Seq2[int, E]func Values[Slice ~[]E, E any](s Slice "Slice ~[]E, E any") iter.Seq[E]func Collect[E any](seq iter.Seq[E] "E any") []E
示例代码如下:
func main() { s1 := []int{1, 2, 3} for k, v := range slices.All(s1) { fmt.Println("k:", k, "v:", v) } for v := range slices.Values(s1) { fmt.Println(v) } // slices.Collect 会将迭代器中的值收集到一个新的切片中并返回它 s2 := slices.Collect(slices.Values([]int{1, 2, 3})) fmt.Println(s2)}
输出结果:
k: 0 v: 1k: 1 v: 2k: 2 v: 3123[1 2 3]
maps 标准库中针对迭代器,新增了:maps.All、maps.Keys、maps.Values、 方法。
函数签名如下:
func All[Map ~map[K]V, K comparable, V any](m Map "Map ~map[K]V, K comparable, V any") iter.Seq2[K, V]func Keys[Map ~map[K]V, K comparable, V any](m Map "Map ~map[K]V, K comparable, V any") iter.Seq[K]func Values[Map ~map[K]V, K comparable, V any](m Map "Map ~map[K]V, K comparable, V any") iter.Seq[V]
示例代码如下:
func main() { m := map[string]int{ "脑子": 1, "进": 2, "煎鱼": 3, "了": 4, "吗": 5, } for k, v := range maps.All(m) { fmt.Println("k:", k, "v:", v) } for k := range maps.Keys(m) { fmt.Println(k) } for v := range maps.Values(m) { fmt.Println(v) }}
输出结果:
// maps.Allk: 吗 v: 5k: 脑子 v: 1k: 进 v: 2k: 煎鱼 v: 3k: 了 v: 4// maps.Keys脑子进煎鱼了吗// maps.Values34512
Go1.23 的迭代器引入,对于 Go 来讲是一个重要的里程碑。虽然在社区上引来了国外社区的大量争议。但也带来了 for-loop 的完整体系的建设,提供了迭代器可遍历万物的概念。
本文链接:http://www.28at.com/showinfo-26-112724-0.htmlGo1.23 新特性:争议最大的 iter 迭代器,可遍历万物!
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 架构设计的简单原则,你学会了吗?
下一篇: 还不会用Java操作远程服务器?