ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Go 1.18+ 泛型库 lo 的 Times 函数:按次数调用回调并收集结果的切片生成器

Go 1.18+ 泛型库 lo 的 Times 函数:按次数调用回调并收集结果的切片生成器 Go 1.18 泛型库 lo 的 Times 函数按次数调用回调并收集结果的切片生成器【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lolo.Times是 Go 泛型库 lo 中位于 slice.go 的核心切片辅助函数它接收一个执行次数count与一个接收下标index的回调iteratee按指定次数依次调用回调并将每次调用的返回值收集成新的切片。本文基于 docs/data/core-times.md 文档结合仓库源码、测试与基准测试系统讲解Times的签名、实现原理、与RepeatBy/Repeat/ForEach的差异、并行与惰性迭代变体以及实际应用场景帮助你准确选用合适的重复执行工具。Times 是什么一次看懂签名与最小示例Times的核心语义来自 docs/data/core-times.md 文档Invokes the predicate n times, returning a slice of results. The predicate receives the index on each call.调用谓词 n 次返回结果切片每次调用时谓词会收到当前下标。在 slice.go 中其完整签名与实现如下// Times invokes the iteratee n times, returning a slice of the results of each invocation. // The iteratee is invoked with index as argument. // Play: https://go.dev/play/p/vgQj3Glr6lT func TimesT any T) []T { result : make([]T, count) for i : 0; i count; i { result[i] iteratee(i) } return result }要点拆解count int执行次数同时决定了返回切片的长度。make([]T, count)在入口处一次性完成内存分配。iteratee func(index int) T每次调用时被传入当前下标i从 0 开始返回值会被原样放入结果切片。T any得益于 Go 1.18 泛型T可以是任意类型包括string、int、结构体、指针、接口等。文档给出的最小示例lo.Times(3, func(i int) string { return strconv.Itoa(i) }) // []string{0, 1, 2}运行后得到[]string{0, 1, 2}回调被执行了 3 次分别收到下标0、1、2每次将下标转换为字符串后收集为结果切片。与 RepeatBy / Repeat / ForEach 的异同如何选择Times常常与重复执行相关的一组函数混淆。该文档在similarHelpers中明确标注了三个相似函数core#slice#foreach、core#slice#repeat、core#slice#repeatby。它们在 slice.go 中均有实现理解差异有助于正确选型。Times vs RepeatBy本质相同、定位不同RepeatBy 的签名与Times几乎一致// RepeatBy builds a slice with values returned by N calls of callback. func RepeatByT any T) []T { result : make([]T, count) for i : 0; i count; i { result[i] callback(i) } return result }从实现看两者行为完全相同都是按次数调用回调并收集结果。区别在于语义命名Times强调调用多少次RepeatBy强调根据回调重复生成值。在实际项目中两者可互换使用选择标准是代码可读性——想表达执行 N 次并收集用Times想表达按 N 次重复构造用RepeatBy。Times vs Repeat克隆 vs 重新生成Repeat 的语义与两者有本质区别// Repeat builds a slice with N copies of initial value. func Repeat[T Clonable[T]](count int, initial T) []T { result : make([]T, count) for i : 0; i count; i { result[i] initial.Clone() } return result }Repeat接收的是一个初始值而非回调通过调用T上的Clone()方法复制 N 份要求类型实现Clonable[T]接口Times/RepeatBy接收的是回调函数每次调用可以返回不同结果如依赖下标计算的值。典型取舍需要 N 份相同值如填充默认配置、初始化固定模板用Repeat需要 N 个按规则生成的值如等差数列、哈希盐、模拟数据用Times。Times vs ForEach返回值的有无ForEach 系列ForEach、ForEachErr、ForEachWhile遍历的是已存在的切片回调无返回值仅做副作用处理Times则是从零开始生成新切片回调必须返回值。若你手头已有一个切片想做映射应改用 Map只有需要凭空生成 N 个元素时才使用Times。结合测试用例理解边界行为slice_test.go 中的TestTimes验证了核心行为func TestTimes(t *testing.T) { t.Parallel() is : assert.New(t) result1 : Times(3, func(i int) string { return strconv.FormatInt(int64(i), 10) }) is.Equal([]string{0, 1, 2}, result1) }该测试确认了两个关键点回调从下标 0 开始Times(3, ...)恰好生成{0, 1, 2}三个元素下标范围是[0, count)返回值严格按回调返回顺序写入结果切片即result[i] iteratee(i)不会重排。同时从实现可以推断的边界行为测试未显式覆盖但由 slice.go 的for i : 0; i count; i循环保证count 0时返回空切片[]T{}make([]T, 0)不为 nil这点在使用时值得注意count 0时同样返回空切片不会 panic由于make([]T, count)预先分配容量Times的辅助内存分配次数为 1远优于逐次append的写法。泛型与预分配从源码看实现策略Times的实现虽然只有数行却体现了两个值得借鉴的工程细节T any全类型支持Times不要求元素类型comparable因此可生成包含函数、切片、通道等不可比较类型的切片这是泛型库相比反射方案如reflect.MakeSlice的核心优势。一次性预分配result : make([]T, count)让内存分配与count精确匹配相比var result []T; result append(result, iteratee(i))的渐进式扩容避免了多次分配与拷贝性能表现更稳定。基准测试 core_slice_bench_test.go 中BenchmarkTimes正是通过lo.Times(n, func(i int) int { return i })对这一路径进行压测。变体一并行执行 parallel.Times当iteratee是 CPU 密集型或存在 IO 等待如逐项请求外部服务时可使用 parallel 包 的并行版本parallel.Timespackage parallel // Times invokes the iteratee n times, returning a slice of the results of each invocation. // The iteratee is invoked with index as argument. // iteratee is called in parallel. func TimesT any T) []T { result : make([]T, count) var wg sync.WaitGroup wg.Add(count) for i : 0; i count; i { go func(_i int) { item : iteratee(_i) result[_i] item wg.Done() }(i) } wg.Wait() return result }实现要点对应 parallel/slice.go每个下标i启动一个 goroutine 执行iteratee通过sync.WaitGroup等待全部完成result[_i] item按下标直接写入由于每个 goroutine 写入的数组下标互不相同_i是各自循环变量的副本因此并发写不同下标不会发生数据竞争wg.Add(count)一次性登记所有 goroutine 完成后wg.Wait()返回保证调用方拿到完整结果。需要注意parallel.Times不保证回调的执行顺序只保证结果按下标正确归位。若回调之间互相依赖或共享可变状态请谨慎使用或加锁。基准测试 parallel_slice_bench_test.go 中的BenchmarkParallelTimes验证了该路径的并行执行效果。变体二惰性序列 it.Times在 it 包基于 Go 1.23iter.Seq的迭代器实现中Times返回的是惰性序列而非切片// Times invokes callback n times and returns a sequence of results. // The transform is invoked with index as argument. func TimesT any T) iter.Seq[T] { return func(yield func(T) bool) { for i : range count { if !yield(callback(i)) { return } } } }与lo.Times的三个关键差异返回值类型不同返回iter.Seq[T]迭代器可用for range消费如for v : range it.Times(3, fn) { ... }惰性求值回调在被遍历时才逐个执行而不是调用Times时一次性执行完。若只取前几个元素配合Take未消费的部分不会执行回调可节省计算量提前终止yield返回false时消费者 break序列立即停止不再调用后续回调。基准测试 it_slice_bench_test.go 中的BenchmarkItTimes覆盖了该迭代路径。需要完整切片结果时用lo.Times需要流式处理或提前退出时用it.Times。实战场景与用法示例场景一生成连续编号的字符串ids : lo.Times(5, func(i int) string { return fmt.Sprintf(user-%03d, i) }) // []string{user-000, user-001, user-002, user-003, user-004}场景二生成等差数列或数学序列seq : lo.Times(10, func(i int) float64 { return float64(i) * 0.5 }) // []float64{0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5}场景三构造模拟数据 / 测试夹具type User struct { ID int Name string } users : lo.Times(3, func(i int) User { return User{ID: i 1, Name: fmt.Sprintf(User%d, i1)} })场景四初始化二维切片 / 网格grid : lo.Times(4, func(row int) []int { return lo.Times(4, func(col int) int { return row*4 col }) }) // 4x4 网格{{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}}场景五生成随机数 / 哈希盐等非确定值salts : lo.Times(8, func(_ int) string { return randomString(16) // 每次调用生成不同随机串 })在仓库中定位 Times 相关代码若要在当前仓库中深入研读Times的完整脉络可按以下路径对照查看核心实现slice.golo.Times、parallel/slice.goparallel.Times、it/seq.goit.Times官方文档docs/data/core-times.md、docs/data/parallel-times.md、docs/data/it-times.md单元测试slice_test.goTestTimes基准测试core_slice_bench_test.go、parallel_slice_bench_test.go、it_slice_bench_test.go相似函数对照Repeat、RepeatBy、RepeatByErr回调返回错误的版本首次出错即返回小结lo.Times是 lo 库中按次数生成切片的基础工具TimesT any T) []T从下标 0 开始逐次调用回调并收集结果预分配内存、支持任意泛型类型。选择建议如下需要按次数生成新的不同值lo.Times/lo.RepeatBy需要 N 份相同克隆值lo.Repeat需要对已有切片做遍历副作用lo.ForEachiteratee 为耗时任务、追求并行吞吐parallel.Times注意并发安全需要流式、可提前退出的生成it.Times返回iter.Seq[T]。掌握这组重复执行工具的差异就能在编写数据处理、初始化与测试代码时选择最贴切、最高效的方案。【免费下载链接】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),仅供参考
返回列表