ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

lo 项目 it 包 RepeatBy 详解:基于迭代器惰性生成序列的 Go 泛型函数

lo 项目 it 包 RepeatBy 详解:基于迭代器惰性生成序列的 Go 泛型函数 lo 项目 it 包 RepeatBy 详解基于迭代器惰性生成序列的 Go 泛型函数【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lo本篇技术指南聚焦 lo 项目it包基于 Go 1.23 迭代器iter.Seq的集合操作库中的RepeatBy函数讲解如何通过回调函数按索引生成指定长度的惰性序列并深入源码剖析其底层实现、提前终止语义以及与切片版RepeatBy、Repeat、Times等相似辅助函数的关系。读完本文你将掌握it.RepeatBy的完整用法、适用场景与底层工作原理能够在自己的迭代器流水线中熟练使用。一、函数概览与签名it.RepeatBy位于it包中其核心语义是通过 N 次调用回调函数callback构建一个序列sequence每次调用时回调都会收到当前的下标index并返回一个类型为T的值这些返回值按顺序组成最终的序列。根据 docs/data/it-repeatby.md 中的 frontmatter 签名信息函数签名如下func RepeatByT any T) iter.Seq[T]逐参数拆解参数类型说明countint回调被调用的次数也是生成序列的元素个数callbackfunc(index int) T接收当前下标、返回元素值的回调函数返回值iter.Seq[T]惰性序列可用for range消费值得注意的关键点泛型约束为T any这是与同包Repeat的重要区别。Repeat要求T lo.Clonable[T]因为要复制同一个初始值而RepeatBy对元素类型没有任何约束任意类型均可使用回调携带下标回调的入参是index int因此生成的值可以依赖位置信息例如index * 2、fmt.Sprintf(item-%d, index1)等天然适合生成带序号的数据返回类型为iter.Seq[T]这是 Go 1.23 引入的标准迭代器类型只有被for range消费时才会真正执行回调属于惰性求值。二、基础用法示例文档 docs/data/it-repeatby.md 给出了两个完整示例完整继承如下。2.1 生成带序号的字符串序列result : it.RepeatBy(3, func(index int) string { return fmt.Sprintf(item-%d, index1) }) var output []string for item : range result { output append(output, item) } // output contains [item-1, item-2, item-3]回调收到的index依次为0、1、2因此index1恰好得到1、2、3拼接后即为item-1、item-2、item-3。2.2 生成基于下标的整数序列result2 : it.RepeatBy(5, func(index int) int { return index * 2 }) var output2 []int for item : range result2 { output2 append(output2, item) } // output2 contains [0, 2, 4, 6, 8]index * 2依次得到0、2、4、6、8可以看到下标从 0 开始这也与 Go 切片、数组的索引习惯保持一致。2.3 借助slices.Collect简化消费仓库中的可运行示例 it/seq_example_test.go 展示了更简洁的收集写法func ExampleRepeatBy() { result : RepeatBy(5, func(i int) string { return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10) }) fmt.Printf(%v, slices.Collect(result)) // Output: [0 1 4 9 16] }这里使用了 Go 标准库slices.Collect将iter.Seq[T]一次性收集为切片配合平方计算输出[0 1 4 9 16]与文档第 2.2 节示例相互印证。三、源码级剖析惰性求值与提前终止3.1 底层实现it.RepeatBy的完整实现在 it/seq.go// RepeatBy builds a sequence with values returned by N calls of transform. // Play: https://go.dev/play/p/i7BuZQBcUzZ func RepeatByT any T) iter.Seq[T] { return func(yield func(T) bool) { for i : range count { if !yield(callback(i)) { return } } } }从源码结构可以看出三个关键实现事实返回的是闭包函数RepeatBy并不立即计算任何值而是返回一个类型为func(yield func(T) bool)的函数这正是iter.Seq[T]的标准形态for i : range count驱动循环循环次数由count决定i依次取0到count-1直接作为回调参数尊重 yield 的布尔返回值每次调用yield(callback(i))后检查返回值若为false立即return实现提前终止early break。需要说明的是it包整体基于 Go 1.23 的迭代器机制it/seq.go 文件头部带有//go:build go1.23构建标签因此使用本函数的前提是 Go 版本不低于 1.23。3.2 提前终止语义的测试验证RepeatBy返回的序列支持消费者中途break这一点由 it/seq_test.go 中的TestRepeatBy显式验证func TestRepeatBy(t *testing.T) { t.Parallel() cb : func(i int) int { return int(math.Pow(float64(i), 2)) } tests : []struct { name string count int expected []int }{ {name: zero, count: 0, expected: nil}, {name: two, count: 2, expected: []int{0, 1}}, {name: five, count: 5, expected: []int{0, 1, 4, 9, 16}}, } for _, tt : range tests { tt : tt //nolint:modernize t.Run(tt.name, func(t *testing.T) { t.Parallel() is : assert.New(t) seq : RepeatBy(tt.count, cb) assertSeqSupportBreak(t, seq) is.Equal(tt.expected, slices.Collect(seq)) }) } }测试覆盖了几个重要行为count 0时返回空序列slices.Collect结果为nil不会调用任何一次回调结果与下标一一对应平方回调下count 5得到[0, 1, 4, 9, 16]即i²支持 break/return测试中的assertSeqSupportBreak定义于 it/lo_test.go会先以break中途退出遍历再以return中途退出遍历验证两者都不会 panic从而保证序列是安全的惰性迭代器。3.3 惰性求值带来的实战收益由于RepeatBy返回的是惰性序列回调只在消费时执行因此可以按需取前几个元素例如count传 10000但消费者只range前 3 个就break回调实际只被调用 3 次避免无谓计算串联下游算子it包中Map、Filter、Take等函数都接受iter.Seq或泛型迭代器接口RepeatBy的返回值可以直接作为它们的输入构成流水线与其他it序列函数组合如it.Take(it.RepeatBy(...), n)取前 n 项等模式。四、与相似辅助函数的对比文档 frontmatter 的similarHelpers字段列出了三个相近函数结合源码对比有助于准确选型。4.1it.Repeat重复同一个克隆值it/seq.go 中的Repeat本质上是RepeatBy的特例func Repeat[T lo.Clonable[T]](count int, initial T) iter.Seq[T] { return RepeatBy(count, func(int) T { return initial.Clone() }) }它把RepeatBy的回调固定为“忽略下标、返回initial.Clone()”因此生成的是 N 个相同值的序列类型约束更严要求T实现lo.Clonable接口见 types.go 中Clonable的定义每次调用Clone()保证元素相互独立若需要每个元素都是独立的副本优先用Repeat若元素依赖下标或需要差异化计算用RepeatBy。4.2 切片版RepeatBy一次性急切求值lo 核心包非it包同样提供RepeatBy实现于 slice.gofunc RepeatByT any T) []T { result : make([]T, count) for i : 0; i count; i { result[i] callback(i) } return result }两者的核心差异在于求值时机与返回类型维度lo.RepeatBy核心包it.RepeatByit 包返回类型[]T切片iter.Seq[T]迭代器求值时机立即调用 count 次回调预分配切片惰性消费时才调用回调内存占用一次性分配count容量无固定分配随消费流动提前终止不支持结果已完整生成支持 break/return文档示例 docs/data/core-repeatby.md 可查看核心包切片版的详细说明。选择原则需要完整切片结果、元素会被反复遍历时用核心包版本构建流水线、追求惰性与按需计算时用it包版本。4.3Times与Repeat的姊妹关系文档similarHelpers中还列出了core#slice#times与iter#sequence#times。slice.go 中核心包Times与RepeatBy形制相似同样是count func(index int) T的签名但语义上更偏向执行 N 次的副作用场景而it包序列版Times的文档见 docs/data/it-times.md。选型时主要看需要的是带下标的差异化生成RepeatBy/Times还是无差别的同值复制Repeat。五、典型实战场景与注意事项5.1 生成测试数据RepeatBy最常用的场景之一是批量构造测试输入ids : it.RepeatBy(100, func(index int) int64 { return int64(1000 index) }) // 生成 1000..1099 的 ID 序列配合slices.Collect即可转成切片供测试断言使用。5.2 生成带序号的对象序列利用index参数可以生成结构体序列例如按顺序编号的订单、座位号、行号等type Row struct { No int Name string } rows : it.RepeatBy(10, func(index int) Row { return Row{No: index 1, Name: fmt.Sprintf(row-%d, index1)} })5.3 注意事项count为负数或 0 的行为for i : range count在count 0时不会进入循环体序列为空回调不会被调用测试用例 zero 即验证了count 0返回nil回调的副作用时机由于惰性求值回调可能只在消费者遍历到对应位置时才执行若回调存在副作用如写日志、累加计数需要理解其执行时机是消费时而非调用RepeatBy时Go 版本要求it包依赖 Go 1.23 的iter标准包使用前请确认go.mod中的 Go 版本当前仓库根目录 go.mod 可查看项目的最低版本约束。六、小结it.RepeatBy是 lo 项目it包中生成按下标规则序列的标准工具它以count func(index int) T的简洁签名返回惰性的iter.Seq[T]底层通过闭包与yield机制支持提前终止与核心包切片版RepeatBy互为惰性 / 急切两种形态并与Repeat、Times组成完整的序列生成家族。在需要生成测试数据、带序号的对象集合或作为迭代器流水线起点时它都是高效且类型安全的选择。延伸阅读核心包切片版RepeatBy文档docs/data/core-repeatby.md同值复制序列Repeat文档docs/data/core-repeat.mdTimes序列版本文档docs/data/it-times.md迭代器序列模块总览docs/docs/iter/sequence.md【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表