Go 1.23: 新包 Iter

 

上周,Go 1.23 进入冻结期,这意味着不会添加任何新功能,并且任何已添加的功能不太可能被删除。这是一个预览即将发生的变化的好机会。
这篇文章,来了解一下 1.23 转正的 iter 包。

This article is first published in the medium MPP plan. If you are a medium user, please follow me in medium. Thank you very much.

在Go 1.22中,引入了range over func实验性功能,但需要通过参数GOEXPERIMENT=rangefunc启用。在Go 1.23中,可以直接使用代码实现这种迭代方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func Backward(s []string) func(yield func(string) bool) {  
   return func(yield func(string) bool) {  
     for i := len(s) - 1; i >= 0; i-- {  
       yield(strings.ToUpper(s[i]))  
     }  
   }  
 }  
   
 func ToUpperByIter() {  
   sl := []string{"hello", "world", "golang"}  
   for v := range Backward(sl) {  
     // do business   
   }  
 }

yield是传递给迭代器的可调用函数的常规名称。
我们考虑一下如何在不使用“iter”包的情况下编写代码来实现相同的功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func Convert[S any, D any](src []S, mapFn func(s S) D) []D {  
     r := make([]D, 0, len(src))  
     for _, i := range src {  
        r = append(r, mapFn(i))  
     }  
     return r  
 }    
     
 func ToUpByString() {  
     sl := []string{"hello", "world", "golang"}  
     s0 := Convert(sl, func(v string) string { return strings.ToUpper(v) })  
     for _, v := range s0 {  
        // do business  
     }  
 }

性能对比

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  huizhou92 git:(master) ✗ go test -bench . -count=3  
 goos: darwin  
 goarch: arm64  
 pkg: huizhou92  
 cpu: Apple M1 Pro  
 BenchmarkToUpByString-10         8568332               128.7 ns/op  
 BenchmarkToUpByString-10         9310351               128.6 ns/op  
 BenchmarkToUpByString-10         9344986               128.5 ns/op  
 BenchmarkToUpByIter-10          12440120                96.22 ns/op  
 BenchmarkToUpByIter-10          12436645                96.25 ns/op  
 BenchmarkToUpByIter-10          12371175                96.64 ns/op  
 PASS  
 ok      huizhou92       8.162s

结果很明显:ToUpperByIter 性能更好,因为它不会重新分配新的slice,使得它比以前的方法更高效。

iter 的目标

iter 包旨在提供统一且高效的迭代方法。它为自定义容器类(尤其是在引入泛型之后)提供了标准的迭代接口,并可以替换一些返回切片的现有 API。通过使用迭代器并利用编译器优化,可以提高性能。此外,它还提供了适合函数式编程风格的标准迭代机制。

如何使用 iter

iter支持两种类型的迭代器:

1
2
3
4
5
6
7
8
9
// 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)

map 包已经使用 iter 来添加了诸如 AllKeys 等方法。这里是它的实现参考:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//https://go.googlesource.com/go/blob/c83b1a7013784098c2061ae7be832b2ab7241424/src/maps/iter.go#L12  
 // All returns an iterator over key-value pairs from m.  
 // The iteration order is not specified and is not guaranteed  
 // to be the same from one call to the next.  
 func All[Map ~map[K]V, K comparable, V any](m Map) iter.Seq2[K, V] {  
     return func(yield func(K, V) bool) {  
        for k, v := range m {  
           if !yield(k, v) {  
              return  
           }  
        }  
     }  
 }    
     
 // Keys returns an iterator over keys in m.  
 // The iteration order is not specified and is not guaranteed  
 // to be the same from one call to the next.  
 func Keys[Map ~map[K]V, K comparable](m Map) iter.Seq[K] {  
     return func(yield func(K) bool) {  
        for k := range m {  
           if !yield(k) {  
              return  
           }  
        }  
     }  
 }

争论

“在我看来,yield 是一个足够复杂的概念,会导致出现大量糟糕的、难以理解的代码。这个建议只提供了语法糖,用于编写语言中已经超出可能范围的内容。我认为这违背了“一个问题 - 一个解决方案”的规则。拜托,让 Go 保持无聊。” 来源

这是社区内常见的反对意见。yield 不容易理解,并且我们可以通过多种方式实现迭代器。

结论

我支持添加iter

iter包为开发人员提供了许多可能性,旨在简化代码并采用更多的函数式编程实践。然而,由于对性能、复杂性和学习曲线的担忧,它的接受度存在分歧。

与任何新工具一样,关键是在提供明显好处的地方平衡其使用,并同时注意潜在缺点。毫无疑问,Go社区将继续探索和辩论如何利用iter的力量而不损害该语言的基本原则。

参考资料

  1. 61405
  2. 56413
  3. iterators_in_go_123
Licensed under CC BY-NC-SA 4.0
最后更新于 Jun 11, 2024 17:33 CST
使用 Hugo 构建
主题 StackJimmy 设计