【go从零单排】迭代器(Iterators)

🌈Don't worry , just coding!
内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。

📗概念

在 Go 语言中,迭代器的实现通常不是通过语言内置的迭代器类型,而是通过自定义类型和方法来实现的。下面是一个简单的示例,展示如何在 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方便理解。

go 复制代码
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

go 复制代码
func genFib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

迭代器Example

go 复制代码
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
}

💪无人扶我青云志,我自踏雪至山巅。

相关推荐
passer__jw7671 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7671 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-71 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
__AtYou__3 小时前
Golang | Leetcode Golang题解之第557题反转字符串中的单词III
leetcode·golang·题解
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
景鹤4 小时前
【算法】递归+回溯+剪枝:78.子集
算法·机器学习·剪枝