
lo 库 Range 系列函数完全指南从 Range 到 RangeFrom、RangeWithSteps 生成数值序列【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/loRange是 lo一个基于 Go 1.18 泛型的 Lodash 风格 Go 库中用于生成整数序列的核心数学工具函数与RangeFrom、RangeWithSteps共同构成完整的数值序列生成能力广泛用于切片初始化、循环计数、分页索引构造等场景。本文将结合源码实现与测试用例深入讲解Range的正向/负向序列语义、底层分配机制并扩展覆盖RangeFrom与RangeWithSteps的泛型用法帮助读者在实际项目中正确、高效地生成任意数值序列。Range从 0 开始的定长整数序列lo.Range是 lo 数学工具集docs/docs/core/math.md中的基础函数其函数签名定义于 docs/data/core-range.mdfunc Range(elementNum int) []int它创建一个指定长度的整数切片从 0 开始递增当传入负数长度时生成递减序列lo.Range(4) // []int{0, 1, 2, 3} lo.Range(-4) // []int{0, -1, -2, -3}实现原理符号决定步长Range的实现在仓库根目录的 math.go 中代码非常精简// Range creates a slice of numbers (positive and/or negative) with given length. func Range(elementNum int) []int { step : Ternary(elementNum 0, -1, 1) length : elementNum * step result : make([]int, length) for i, j : 0, 0; i length; i, j i1, jstep { result[i] j } return result }其核心逻辑可以拆解为三步确定步长通过Ternary(elementNum 0, -1, 1)判断元素个数的正负。elementNum为正时步长为1为负时步长为-1。Ternary本身也是 lo 提供的条件表达式辅助函数实现于 condition.go。计算长度length : elementNum * step将负数取绝对值得到切片实际容量。例如Range(-4)的length (-4) × (-1) 4。填充序列使用for i, j : 0, 0; ...双变量循环i跟踪索引、j跟随步长累加一次性完成切片填充避免了额外的 append 扩容开销。边界情况零长度的行为当elementNum为 0 时length为 0make([]int, 0)会返回一个空切片。这在测试用例 math_test.go 中有明确验证测试表包含三组用例——正数4→{0,1,2,3}、负数-4→{0,-1,-2,-3}以及零0→ 空切片nil零长度用例通过is.Empty(result)断言通过。从测试结构看Range及其系列函数在仓库中被视为一等公民每个函数都有对应的独立测试函数TestRange、TestRangeFrom、TestRangeWithSteps且全部使用t.Parallel()并发执行保证并发安全语义。使用场景Range最典型的应用是构造固定长度的索引切片例如分页组件中的页号序列、初始化棋盘/矩阵的下标集合或配合lo.Map生成占位数据// 生成 5 页的分页索引 [0,1,2,3,4] pages : lo.Range(5) // 配合 Map 生成业务数据 rows : lo.Map(lo.Range(10), func(i int, _ int) string { return fmt.Sprintf(row-%d, i) })负长度生成递减序列的特性可以用于倒计时或反向遍历辅助countdown : lo.Range(-3) // []int{0, -1, -2}RangeFrom指定起始值的定长序列当需要从非零起始值生成定长序列时使用RangeFrom。其签名定义于 docs/data/core-rangefrom.mdfunc RangeFromT constraints.Integer | constraints.Float []T它创建一个从start开始、长度由elementNum决定的切片负长度同样产生递减序列lo.RangeFrom(1, 5) // []int{1, 2, 3, 4, 5}泛型支持整数与浮点通用RangeFrom是泛型函数类型参数T被约束为constraints.Integer | constraints.Float该约束定义在 internal/constraints/constraints.go覆盖int、int8、int64、uint、float32、float64等所有内置数值类型。实现位于 math.gofunc RangeFromT constraints.Integer | constraints.Float []T { step : Ternary(elementNum 0, -1, 1) length : elementNum * step result : make([]T, length) for i, j : 0, start; i length; i, j i1, jT(step) { result[i] j } return result }与Range的差异仅在两点初始值j从start出发以及步长通过T(step)显式转换为目标类型T。这保证了浮点类型如float64也能正确累加。测试 math_test.go 同时验证了int与float64两种类型RangeFrom(1, 5)得到升序序列RangeFrom(-1, -5)得到降序序列{-1,-2,-3,-4,-5}RangeFrom(10, 0)返回空切片。RangeWithSteps指定起点、终点与步长的序列当需要精确控制步长而非固定 ±1时使用RangeWithSteps。其签名定义于 docs/data/core-rangewithsteps.mdfunc RangeWithStepsT constraints.Integer | constraints.Float []T它创建一个从start开始、到end结束不包含end、按step递增或递减的切片lo.RangeWithSteps(0, 20, 5) // []int{0, 5, 10, 15}方向匹配与空结果规则RangeWithSteps的行为规则比前两者更丰富实现在 math.gostart end或step 0直接返回空切片[]T{}。步长为 0 会导致无限循环因此被显式拦截。升序场景start end若step 0方向不匹配返回空切片否则从start循环累加step直到不小于end。降序场景start end若step 0方向不匹配返回空切片否则从start循环累减直到不大于end。因此以下调用都会返回空切片lo.RangeWithSteps(0, 5, 0) // step 为 0 lo.RangeWithSteps(0, 5, -1) // 升序却给负步长 lo.RangeWithSteps(5, 0, 1) // 降序却给正步长而合法降序用法为lo.RangeWithSteps(10, 0, -2) // []int{10, 8, 6, 4, 2}预分配容量的巧妙处理值得注意的实现细节是 math.go 中的容量预分配逻辑capacity : func(count, delta T) int { // Use math.Ceil instead of (count-1)/delta1 because integer division // fails for floats (e.g., 5.5/2.52.2 → ceil3, not 2). return int(math.Ceil(float64(count) / float64(delta))) }代码注释明确解释了选择math.Ceil而非(count-1)/delta1的原因后者的整数除法在浮点场景下会出错例如5.5/2.52.2向下取整得到 2而实际需要 3 个元素。这个细节体现了该函数对浮点类型支持的严谨性也提示读者当T为浮点类型时RangeWithSteps返回的元素个数可能与直觉略有出入建议在关键业务中先验证结果长度。it 包变体Range 的惰性序列版本仓库还提供基于 Go 1.23iter.Seq的迭代器版本it/math.go位于it包文件名含//go:build go1.23构建标签仅在 Go 1.23 环境下生效// Range creates a sequence of numbers (positive and/or negative) with given length. func Range(elementNum int) iter.Seq[int] { step : lo.Ternary(elementNum 0, -1, 1) length : elementNum * step return func(yield func(int) bool) { for i, j : 0, 0; i length; i, j i1, jstep { if !yield(j) { return } } } }该版本与切片版共享相同的步长/长度计算逻辑对应文档 docs/data/it-range.md区别在于惰性求值序列只在被消费时按需生成适合构造大范围序列但只取前几个元素的场景提前终止yield返回false时立即停止迭代天然支持break语义不产生堆分配无需make切片内存开销更小。it包同样提供RangeFrom与RangeWithSteps的迭代器版本分别见 docs/data/it-rangefrom.md 与 docs/data/it-rangewithsteps.md方向匹配规则与切片版完全一致。性能特征与基准测试仓库的基准测试 benchmark/core_math_bench_test.go 为三个函数分别编写了BenchmarkRange、BenchmarkRangeFrom、BenchmarkRangeWithSteps均以lengths中的多组数据规模压测。从实现结构看Range与RangeFrom使用make一次性预分配一次分配、零扩容而RangeWithSteps也通过capacity闭包精确预分配因此三个函数在正常参数下都是单次分配的最优路径。基准测试还按数据规模分b.Run(strconv.Itoa(n))子测试便于观察不同长度下的分配与填充耗时。需要说明的是本文不引用具体基准数据读者如需对比可自行在本地运行go test -benchBenchmarkRange -benchmem基准测试目录位于 benchmark/。相关辅助函数Range系列在文档体系中与以下函数互为关联见 docs/data/core-range.md 的similarHelpers字段lo.Times重复调用回调指定次数并收集结果可视为「生成序列 Map」的合成操作lo.Repeat/lo.RepeatBy按次数重复元素或按回调重复生成docs/data/core-repeat.md、docs/data/core-repeatby.md。实际编码时可按需组合例如先用lo.Range生成索引序列再叠加lo.Map、lo.Filter等函数完成流水线式处理。快速上手在 Go 1.18 项目中安装 lo 并导入即可使用安装命令见 README.mdgo get github.com/samber/lov1import github.com/samber/lo // 升序定长序列 lo.Range(4) // []int{0, 1, 2, 3} // 降序定长序列 lo.Range(-4) // []int{0, -1, -2, -3} // 指定起点的定长序列支持整数与浮点 lo.RangeFrom(1, 5) // []int{1, 2, 3, 4, 5} // 指定起点、终点与步长不含终点 lo.RangeWithSteps(0, 20, 5) // []int{0, 5, 10, 15}若项目已升级至 Go 1.23还可使用it包的惰性序列版本处理大范围数据兼顾内存效率与中断语义。【免费下载链接】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),仅供参考