【go从零单排】迭代器(Iterators)
🌈Don’t worry , just coding!
内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。
📗概念
在 Go 语言中,迭代器的实现通常不是通过语言内置的迭代器类型,而是通过自定义类型和方法来实现的。下面是一个简单的示例,展示如何在 Go 中实现一个迭代器。
💻代码
迭代器
package main
import "fmt"
// IntSliceIterator 是一个自定义的迭代器,用于迭代整数切片
type IntSliceIterator struct {
//data:存储要迭代的整数切片。
//index:当前迭代的位置。
data []int
index int
}
// 定义NewIntSliceIterator函数,输入data切片类型为int
// 返回一个指向 IntSliceIterator 结构体的指针。
func NewIntSliceIterator(data []int) *IntSliceIterator {
//&IntSliceIterator{} 创建了一个新的 IntSliceIterator 实例并返回
return &IntSliceIterator{
data: data,
index: 0,
}
}
// HasNext 检查是否还有下一个元素
func (it *IntSliceIterator) HasNext() bool {
return it.index < len(it.data)
}
// 定义Next函数 返回下一个元素并移动迭代器
// 输入*IntSliceIterator指针,赋值给it变量
// 返回一个int类型
func (it *IntSliceIterator) Next() int {
if !it.HasNext() {
//如果没有更多元素,使用 panic 抛出一个错误
panic("No more elements")
}
//从data 切片中获取当前索引 it.index 指向的元素,并将其赋值给 value。
value := it.data[it.index]
//将 index 增加 1,下次调用时指向下一个元素。
it.index++
return value
}
func main() {
data := []int{1, 2, 3, 4, 5}
iterator := NewIntSliceIterator(data)
for iterator.HasNext() {
fmt.Println(iterator.Next())
}
}
//输出
//1
//2
//3
//4
//5
yeild
在python中yeild表示本次执行结束并返回值,类似于return,yeild和return不同的地方在于yeild可以优雅的返回每次调用时的值。
在go中没有yeild关键字,我们用yeild方便理解。
package main
import (
"fmt"
"iter"
"slices"
)
// 这不是来了么,定义一个泛型List 任意类型的struct
type List[T any] struct {
head, tail *element[T]
}
// 老样子,定义element泛型struct 任意类型,是一个链表
type element[T any] struct {
next *element[T]
val T
}
// 链表的push方法
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
// 输入为lst,类型为 *List[T],返回一个函数,这个函数的类型是 iter.Seq[T],是一个迭代器
func (lst *List[T]) All() iter.Seq[T] {
//返回值是一个匿名函数,接受一个参数 yield,这个参数是一个函数类型,接受一个类型为 T 的参数并返回一个布尔值
return func(yield func(T) bool) {
//初始化 e 为链表的头节点
//循环条件为 e != nil
//e = e.next 将e指向链表下一个节点
for e := lst.head; e != nil; e = e.next {
//调用yield,如果yield 返回 false,则退出循环,结束遍历
if !yield(e.val) {
return
}
}
}
}
// 斐波那契数列生成
// 定义函数genFib
// 输入参数没有
// 返回一个函数 int类型
func genFib() iter.Seq[int] {
//返回一个匿名函数func,该匿名函数接受一个参数 yield,这个参数是一个函数类型,接受一个 int 类型的参数并返回一个布尔值。
return func(yield func(int) bool) {
//赋值
a, b := 1, 1
//调用yield,如果yield 返回 false,则退出循环
for {
if !yield(a) {
return
}
a, b = b, a+b
}
}
}
func main() {
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
for e := range lst.All() {
fmt.Println(e)
}
//lst 是一个链表,调用 All() 方法会返回一个迭代器
//这个生成器会遍历链表中的所有元素,并通过 yield 函数逐个返回这些元素。
all := slices.Collect(lst.All()) //slices.Collect(...) 函数遍历生成器,收集所有元素
fmt.Println("all:", all)
//调用genFib,赋值给n
for n := range genFib() {
//当n>=10跳出循环
if n >= 10 {
break
}
fmt.Println(n)
}
}
//输出
//10
//13
//23
//all: [10 13 23]
//1
//1
//2
//3
//5
//8
💡 Tips小知识点
迭代器和生成器
在 Go 语言中,生成器和迭代器是处理序列数据的两种不同概念,虽然它们有相似之处,但在实现和使用上有一些关键的区别。
- 生成器是一种函数,它可以逐步生成一系列值,而不是一次性返回所有值。生成器通常使用 yield 关键字(在 Go 中通常通过返回一个函数来模拟)来返回下一个值,并保持其状态,以便在下一次调用时继续执行。
- 生成器通常在需要时生成值,适用于需要惰性求值的场景。调用生成器时,可以获取下一个值,而不需要事先知道所有值。
- 生成器通过闭包保持状态,允许在多次调用之间保留局部变量的值。
- 迭代器是一种对象,它实现了特定的接口(通常包含 Next() 方法),用于遍历集合中的元素。迭代器维护一个内部状态,允许用户逐步访问集合的每个元素。
- 迭代器通常用于遍历整个集合,通过调用 Next() 方法来获取下一个元素,直到没有更多元素可供访问。
- 迭代器通过结构体的字段来管理状态,通常在结构体中维护当前元素的指针或索引。
生成器Example
func genFib() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
迭代器Example
type Iterator struct {
current *Node
}
func (it *Iterator) Next() *Node {
if it.current == nil {
return nil
}
node := it.current
it.current = it.current.next
return node
}
💪无人扶我青云志,我自踏雪至山巅。