介绍Go1.23中的iter包

新的iter包怎么样?对我们来说是好是坏?

photo by Kimon Maritz on Unsplash

The friendship that can cease has never been real.
— Jerome

介绍

Go 1.23的新iter包引入了迭代器的概念和语言级别的支持。这个迭代器功能对现有的Go变成方式来说是一个非常重要的扩展。

在iter包中,定义了主标准的两种迭代器格式:

1
2
3
4
5
6
7
8
// Seq is an iterator over sequences of individual values.
// When called as seq(yield), seq calls yield(v) for each value v in the sequence,
// stopping early if yield returns false.
type Seq[V any] func(yield func(V) bool)
// Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs.
// When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence,
// stopping early if yield returns false.
type Seq2[K, V any] func(yield func(K, V) bool)

src/iter/iter.go - go - Git at Google

以下是主要的操作:

1. 提供统一的迭代器接口iter包定义了SeqSeq2迭代器,前者用于单值序列的迭代,后者用于键值对序列的迭代。这为各种数据结构,如数组,切片,map,channel等提供了统一的迭代方式。

2. 封装底层细节:用户无需了解底层的数据结构,只需要使用Seq或者Seq2即可实现迭代,这极大地简化了代码的编写。

3. 泛型的引入iter包中的迭代器采用了新引入的泛型机制,可以处理任何类型的数据,增加了代码的通用性和重用性。

4. 内置了错误处理:迭代过程中的错误可以被捕获并处理,使得编程更加安全。

例子

如果用新的特性来实现:打印低于1000的斐波那契数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
fibo := func(yield func(x int) bool) {
f0, f1 := 0, 1
for yield(f0) {
f0, f1 = f1, f0+f1
}
}

for x := range fibo {
if x >= 1000 {
break
}
fmt.Printf("%d ", x)
}
}

Better Go Playground

在这段代码中,yield 可以看成一个过滤器的角色,只有当它返回 true 时,我们才打印出我们正在生成的斐波那契数。这给了调用者(这里是 main 函数)很大的灵活性,因为它们可以决定何时停止生成和打印斐波那契数。
关于yield 的更多用法你可以参考:Iterators in Go — Bitfield Consulting; This article describes an experimental extension to the Go language: range over func. 当然在 golang 1.23 中这个特性将直接工作。

如果以往通过channel来实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
fibo := func() <-chan int {
ch := make(chan int)
go func() {
f0, f1 := 0, 1
for {
ch <- f0
f0, f1 = f1, f0+f1
if f0 >= 1000 {
close(ch)
break
}
}
}()
return ch
}
for x := range fibo() {
fmt.Printf("%d ", x)
}
}

显然,iter 包看起来会更简单,但是对于不想接受新事物的人来说,理解起来会更困难,需要一定的成本。这不符合一部分人关于KISS 原则的坚持。

争议与思考

I’ve always considered Go (and the community) to be very cautious about making languages changes, not to add duplicate ways of representing the same thing. Pushing a cohesive and simple design. I’m curious, is it just me feeling like there is a major shift in principles here and is there anybody else who has had a look at this and thought the built-in channels are the obvious answer here?
reddit.com/r/golang/comments/1dfpx56/am_i_the_only_one_who_thinks_channels_are_the/

同样在iter: new package for iterators · Issue #61897 · golang/go · GitHub 也有一些不同的声音。

尽管iter包的实现引起了一些争议,比如需要额外的iter.Seq2用于键值对的迭代,但总体上,这个功能描述了一个能将Go语言中的迭代行为标准化的一个明确的路径。In summary, then, iterators are neat.

参考

iter: new package for iterators · Issue #61897 · golang/go · GitHub
Iterators in Go — Bitfield Consulting
reddit.com/r/golang/comments/1dfpx56/am_i_the_only_one_who_thinks_channels_are_the/

English post: https://programmerscareer.com/golang-iter/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:原文在 2024-06-16 21:26 时创作于 https://programmerscareer.com/golang-iter/. 本文为作者原创,转载请注明出处。

golang 中你应该知道的时间控制 使用VSCode Profiles快速切换不同开发语言

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×