综合 Go 1.23 迭代器,统一标准,改善 Go 生态系统

2024-11-18 17:24:19 +0800 CST views 648

Go 1.23 迭代器,统一标准,改善 Go 生态系统

前言

Go 1.23 版本在 2024 年 8 月 14 日发布,带来了多项重大更新。其中,最值得关注的就是统一的迭代器标准。本篇文章将重点介绍 Go 1.23 版本引入的标准迭代器,分析其对 Go 生态系统的改善。

让我们准备一杯咖啡,深入探讨这一新功能。

为什么引入标准迭代器

虽然迭代器在 Go 语言中并不是新概念,很多库如 bufio.Scannerdatabase.Rowsfilepath.Walk 等都实现了迭代器,但这些迭代器的实现各不相同,使用时需要重新学习每个库的具体实现方式。通过引入统一的标准迭代器,开发者只需掌握一种通用的迭代器使用方法,就可以适应所有场景。这大大提升了 Go 语言的开发体验和一致性。

迭代器的定义

在 Go 1.23 中,迭代器分为以下三种签名形式:

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

只要函数符合以上形式之一,它就可以被称为迭代器。Go 1.23 中的迭代器设计为推迭代器,通过调用 yield 函数推送元素。如果 yield 返回 false,则迭代终止。

示例代码

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
            }
        }
    }
}

这个函数会倒序遍历切片 s,通过 yield 函数推送每个元素。

Range Over Function Types(对函数类型的遍历)

在 Go 1.23 中,for-range 循环新增了对函数类型的支持,前提是函数实现了迭代器接口。这意味着可以直接对函数进行迭代操作,简化了迭代器的使用。

示例代码

package main

import "fmt"

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

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
            }
        }
    }
}

输出:

1 程序员茄子
0 程序员

iter

Go 1.23 新增了 iter 包,定义了两种迭代器类型 SeqSeq2,用于不同场景下的迭代操作。

package iter

type Seq[V any] func(yield func(V) bool)
type Seq2[K, V any] func(yield func(K, V) bool)
  • Seq:用于单值迭代。
  • Seq2:用于键值对的迭代。

Set 集合的迭代器实现

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]) All() iter.Seq[E] {
    return func(yield func(E) bool) {
        for v := range s.m {
            if !yield(v) {
                return
            }
        }
    }
}

完整示例代码

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]) 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("程序员")
    set.Add("程序员茄子")

    for v := range set.All() {
        fmt.Println(v)
    }
}

输出:

程序员
程序员茄子

拉迭代器

拉迭代器由调用方主动请求数据,而推迭代器则是由数据容器推送数据。在 Go 1.23 中,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("程序员")
    set.Add("程序员茄子")

    next, stop := iter.Pull(set.All())
    for {
        v, ok := next()
        if !ok {
            break
        }
        fmt.Println(v)
        stop()
    }
}

标准库中的迭代器函数

Go 1.23 为 slicesmaps 包增加了多个与迭代器相关的函数,以简化对常见数据结构的迭代操作。

slices 包新增的函数

  • All([]E) iter.Seq2[int, E]
  • Values([]E) iter.Seq[E]
  • Collect(iter.Seq[E]) []E

maps 包新增的函数

  • All(map[K]V) iter.Seq2[K, V]
  • Keys(map[K]V) iter.Seq[K]
  • Values(map[K]V) iter.Seq[V]

小结

Go 1.23 引入了标准迭代器机制,统一了迭代器的设计,提升了代码的一致性和可维护性。虽然新增的迭代器功能可能会增加一定的学习成本,但它极大地简化了 Go 生态中迭代操作的复杂性,尤其是配合 iter 包的使用,开发者可以更轻松地处理各种迭代场景。

你对 Go 1.23 中的迭代器有何看法?欢迎在评论区分享你的见解。

参考资料

相关链接引用

推荐文章

H5端向App端通信(Uniapp 必会)
2025-02-20 10:32:26 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
前端代码规范 - Commit 提交规范
2024-11-18 10:18:08 +0800 CST
imap_open绕过exec禁用的脚本
2024-11-17 05:01:58 +0800 CST
PHP解决XSS攻击
2024-11-19 02:17:37 +0800 CST
使用 Git 制作升级包
2024-11-19 02:19:48 +0800 CST
ElasticSearch简介与安装指南
2024-11-19 02:17:38 +0800 CST
Go的父子类的简单使用
2024-11-18 14:56:32 +0800 CST
MySQL设置和开启慢查询
2024-11-19 03:09:43 +0800 CST
使用Vue 3实现无刷新数据加载
2024-11-18 17:48:20 +0800 CST
10个几乎无人使用的罕见HTML标签
2024-11-18 21:44:46 +0800 CST
2025年,小程序开发到底多少钱?
2025-01-20 10:59:05 +0800 CST
Vue中的样式绑定是如何实现的?
2024-11-18 10:52:14 +0800 CST
虚拟DOM渲染器的内部机制
2024-11-19 06:49:23 +0800 CST
如何将TypeScript与Vue3结合使用
2024-11-19 01:47:20 +0800 CST
如何在Vue 3中使用Ref访问DOM元素
2024-11-17 04:22:38 +0800 CST
curl错误代码表
2024-11-17 09:34:46 +0800 CST
JavaScript 策略模式
2024-11-19 07:34:29 +0800 CST
程序员茄子在线接单