Go 1.23 新版本新特性之迭代器

为什么引入标准迭代器

在 Go 语言中,迭代器并非新概念,但现有的迭代器设计和使用方式各不相同。为了统一迭代器的标准化形式,Go 1.23 版本引入了标准迭代器,使得开发者只需掌握一种迭代器的定义和使用方式,便可以适应所有迭代器。

迭代器的定义

在 Go 1.23 中,迭代器是指符合以下三种函数签名之一的函数:

func(yield func() bool)
func(yield func(V) bool)
func(yield func(K, V) bool)

迭代器分为推迭代器(push iterator)和拉迭代器(pull iterator)。上述设计是典型的推迭代器,通过调用 yield 函数逐步推出一系列值。

代码示例

func Backward[E any](s []E) func(yield func(int, E) bool) {
    return func(yield func(int, E) bool) {
        for i := len(s) - 1; i >= 0; i-- {
            if !yield(i, s[i]) {
                return
            }
        }
    }
}

Range Over Function Types

在 Go 1.23 中,for-range 循环得到了改进,新增了对函数类型的支持。这意味着我们可以使用 for-range 循环来接收迭代器推出的值。

代码示例

package main

import "fmt"

func main() {
    s := []string{"程序员", "陈明勇"}
    for i, v := range Backward(s) {
        fmt.Println(i, v)
    }
}

// Backward 倒序迭代
func Backward[E any](s []E) func(yield func(int, E) bool) {
    return func(yield func(int, E) bool) {
        for i := len(s) - 1; i >= 0; i-- {
            if !yield(i, s[i]) {
                return
            }
        }
    }
}

iter

Go 1.23 版本新增了一个 iter 包,定义了两种迭代器类型:SeqSeq2,用于处理不同的迭代场景。

Seq 和 Seq2 的区别

  • Seq[V any]:推出单个元素,例如切片的索引或映射中的键。
  • Seq2[K, V any]:推出一对元素,例如切片中的索引和值,或者映射中的键值对。

在 Set 集合中使用迭代器的案例

代码示例

package main

import (
    "fmt"
    "iter"
)

type Set[E comparable] struct {
    m map[E]struct{}
}

func NewSet[E comparable]() Set[E] {
    return Set[E]{m: make(map[E]struct{})}
}

func (s Set[E]) Add(e E) {
    s.m[e] = struct{}{}
}

func (s Set[E]) Remove(e E) {
    delete(s.m, e)
}

func (s Set[E]) Contains(e E) bool {
    _, ok := s.m[e]
    return ok
}

func (s Set[E]) All() iter.Seq[E] {
    return func(yield func(E) bool) {
        for v := range s.m {
            if !yield(v) {
                return
            }
        }
    }
}

func main() {
    set := NewSet[string]()
    set.Add("Go1.23")
    set.Add("迭代器")
    for v := range set.All() {
        fmt.Println(v)
    }
}

拉迭代器

拉迭代器与推迭代器相反,由调用方主动请求数据。iter 包提供了 Pull 函数,可以将标准(推)迭代器转换为拉迭代器。

代码示例

package main

import (
    "fmt"
    "iter"
)

type Set[E comparable] struct {
    m map[E]struct{}
}

// ... 省略其他方法 ...

func (s Set[E]) All() iter.Seq[E] {
    return func(yield func(E) bool) {
        for v := range s.m {
            if !yield(v) {
                return
            }
        }
    }
}

func main() {
    set := NewSet[string]()
    set.Add("Go1.23")
    set.Add("迭代器")
    next, stop := iter.Pull(set.All())
    for {
        v, ok := next()
        if !ok {
            break
        }
        fmt.Println(v)
        stop()
    }
}

标准库新增的迭代器函数

随着迭代器的引入,slicesmaps 包也新增了一些与迭代器一起使用的函数。这些函数的详细用法可以参考相应的包文档。

总结

Go 1.23 版本通过引入标准迭代器,统一了迭代器的设计和使用方式,解决了各自为政的问题,进一步优化了 Go 的生态系统。虽然迭代器的引入可能会对代码可读性产生一定影响,但 Go 官方通过新增 iter 包和在 slicesmaps 包中提供便捷函数,简化了迭代器的使用。


相关链接引用:

打 赏