Go lo 库 it.ContainsBy 详解:基于谓词在 iter.Seq 序列上判断存在性
Go lo 库 it.ContainsBy 详解基于谓词在 iter.Seq 序列上判断存在性【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/loit.ContainsBy是 lo 库it子包基于 Go 1.23 引入的iter.Seq迭代器协议提供的集合存在性判断辅助函数它接收任意泛型序列与一个谓词函数只要序列中存在任意一个元素使谓词返回true就立即返回true。阅读本文后你将掌握该函数的完整签名、所有典型用法数值、字符串、结构体、指针、空集合等场景、底层惰性求值与提前终止的实现细节以及它与Contains、SomeBy、EveryBy、NoneBy等姊妹函数的关系与选型依据。函数签名与语义it.ContainsBy定义在 it/intersect.go签名如下func ContainsByT any bool) boolcollection iter.Seq[T]输入集合。参数类型是 Go 1.23 标准库的iter.Seq[T]即func(func(T) bool)的别名因此任何符合该协议的值都可以传入——slices.Values(slice)构造的切片序列、range-over-func迭代器、it.Map/it.Filter等 lo 辅助函数返回的惰性序列都可以。T any意味着元素类型没有任何约束连不可比较类型如struct、slice也可以传入。predicate func(item T) bool判断每个元素是否命中的谓词函数。谓词对序列中的元素按顺序逐个调用。返回值存在命中元素时返回true序列为空或没有任何元素命中时返回false。官方文档对语义的完整描述是Returns true if predicate function returns true for any element in the collection. Will iterate through the entire sequence if predicate never returns true.若任一元素使谓词返回true则返回true若谓词从未返回true则会遍历完整序列。与同文件中的it.Containsit/intersect.go#L14-L16相比二者是精确匹配 vs 条件匹配的关系Contains要求T comparable内部直接委托给ContainsBy把相等比较封装成谓词func ContainsT comparable bool { return ContainsBy(collection, func(item T) bool { return item element }) }因此只要判断等不等于某个值用Contains更简洁只要判断满足某个条件大小比较、前缀匹配、字段相等、空指针检查等就用ContainsBy。源码实现惰性求值与提前终止ContainsBy的完整实现只有不到 10 行it/intersect.go#L20-L28// ContainsBy returns true if predicate function return true. // Will iterate through the entire sequence if predicate never returns true. func ContainsByT any bool) bool { for item : range collection { if predicate(item) { return true } } return false }从这段实现可以确认三个关键行为短路short-circuit求值一旦某个元素使谓词返回true函数立即return true序列的后续元素不会被拉取谓词也不会再被调用。对于惰性序列这意味着后续昂贵的元素生成逻辑根本不会执行。最坏情况遍历全序列只有当所有元素都不命中或序列耗尽时才返回false这正是文档中 Will iterate through the entire sequence if predicate never returns true 的出处。零额外内存分配实现中没有任何 map 或 slice 分配与同文件中Intersect、Uniq这类需要分配 map 的集合运算源码注释明确标注 Will allocate a map large enough...不同ContainsBy是纯流式、O(1) 空间复杂度的操作。短路特性可以用无限序列直观验证——下面的序列理论上有无穷多个元素但查找偶数在第一个偶数处就停止了程序能正常结束// 惰性序列只有找到偶数时才停止不会遍历无穷 seq : it.Times(1_000_000, func(i int) int { return i * i - 2 }) hasEven : it.ContainsBy(seq, func(n int) bool { return n%2 0 }) // true远早于遍历完构建序列的注意事项it包的文件带有//go:build go1.23构建标签见 it/intersect.go 首行而仓库go.mod中模块整体声明go 1.18go.mod。也就是说核心lo包面向 Go 1.18而it包含ContainsBy只有在 Go 1.23 工具链下才会参与编译。这是使用该函数的首要环境前提。关于如何从切片构建iter.Seq原文档示例中使用了it.Slice(slice)的写法但请注意当前仓库中it.Slice的签名是SliceT any, I ~func(func(T) bool) Iit/seq.go#L891是取序列第 start 到 end 段的子集辅助函数与由切片构造序列不同。在当前源码下由切片构造序列推荐直接采用标准库的slices.Values这与it包测试文件中的构造方式一致如 it/intersect_test.go 中用内部values(...)辅助函数构建iter.Seq。下文示例统一采用slices.Values构造序列其余逻辑与原文档示例完全一致。典型用法与示例以下示例继承自文档原始示例docs/data/it-containsby.md序列构造改为slices.Values以匹配当前仓库的it.Slice语义检查数值集合是否含偶数import ( iter slices ) func exampleHasEven() bool { var numbers iter.Seq[int] numbers slices.Values([]int{1, 3, 5, 7, 9}) hasEven : it.ContainsBy(numbers, func(n int) bool { return n%2 0 }) // hasEven: false numbers slices.Values([]int{1, 3, 5, 8, 9}) hasEven it.ContainsBy(numbers, func(n int) bool { return n%2 0 }) // hasEven: true return hasEven }按字符串前缀判断var words iter.Seq[string] slices.Values([]string{hello, world, go, lang}) hasPrefix : it.ContainsBy(words, func(s string) bool { return strings.HasPrefix(s, go) }) // hasPrefix: true对结构体按字段判断由于T any无comparable约束结构体集合可直接作为输入type Person struct { Name string Age int } var people iter.Seq[Person] slices.Values([]Person{ {Name: Alice, Age: 30}, {Name: Bob, Age: 25}, {Name: Charlie, Age: 35}, }) hasAge30 : it.ContainsBy(people, func(p Person) bool { return p.Age 30 }) // hasAge30: true hasAge40 : it.ContainsBy(people, func(p Person) bool { return p.Age 40 }) // hasAge40: false属性长度、负数、格式校验与空集合var stringsSeq iter.Seq[string] slices.Values([]string{apple, banana, cherry}) hasLongString : it.ContainsBy(stringsSeq, func(s string) bool { return len(s) 5 }) // hasLongString: true var numbers iter.Seq[int] slices.Values([]int{1, -2, 3, 4, -5}) hasNegative : it.ContainsBy(numbers, func(n int) bool { return n 0 }) // hasNegative: true var emails iter.Seq[string] slices.Values([]string{userexample.com, invalid-email, testdomain.org}) hasValidEmail : it.ContainsBy(emails, func(email string) bool { return strings.Contains(email, ) strings.Contains(email, .) }) // hasValidEmail: true var empty iter.Seq[int] slices.Values([]int{}) hasAny : it.ContainsBy(empty, func(n int) bool { return n 0 }) // hasAny: false —— 空集合恒返回 false在指针集合中检测 nilfunc ptr(v int) *int { return v } var ptrs iter.Seq[*int] slices.Values([]*int{ptr(5), nil, ptr(10)}) hasNil : it.ContainsBy(ptrs, func(p *int) bool { return p nil }) // hasNil: true测试用例对行为的验证it包的单元测试TestContainsByit/intersect_test.go#L37-L92覆盖了两个维度的场景可作为行为依据结构体类型子测试对{A: 1, B: 1}, {A: 2, B: 2}, {A: 3, B: 3}的序列谓词t.A 1 t.B 2跨字段组合无匹配期望false谓词t.A 2 t.B 2单元素全字段匹配期望true。这验证了谓词可以做任意多字段联合判断。字符串类型子测试对aaa, bbb, ccc谓词t ccc期望truet ddd期望false验证了命中与未命中两条路径。两个子测试均使用t.Parallel()并行执行并以testify/assert断言结果与 lo 仓库整体测试风格一致。与姊妹函数的关系与选型it包在 it/intersect.go 中成组提供了一整套谓词式集合判断函数ContainsBy是其中的一个。从源码看各函数的量化语义如下函数定义位置语义空集合返回值it.Containsit/intersect.go#L14存在等于给定值的元素T comparable内部委托ContainsByfalseit.ContainsByit/intersect.go#L20存在使谓词为true的元素falseit.SomeByit/intersect.go#L82存在使谓词为true的元素注释另明确空集合返回falsefalseit.EveryByit/intersect.go#L53所有元素均使谓词为truetrue空真it.NoneByit/intersect.go#L109没有任何元素使谓词为truetrueit.CountByit/seq.go#L841统计谓词为true的元素个数需遍历全序列0从源码结构看ContainsBy与SomeBy的函数体完全相同逐元素判断、命中即返回true二者是同一语义下的两个命名可以推断这是为了对齐 Lodash 的some命名习惯而保留的别名式变体实际编码中任选其一即可。选型建议判断是否有某个条件成立ContainsBy/SomeBy判断是否全部满足EveryBy判断是否全部不满足NoneBy需要满足条件的数量CountBy注意它会遍历完整序列无法短路只需要值相等比较且元素可比较优先Contains少写一层闭包。此外核心lo包在切片场景下有对应的lo.ContainsBy文档见 docs/data/core-containsby.md其文档 frontmatter 中也将it.ContainsBy标注为variantHelpersdocs/data/it-containsby.md 的variantHelpers字段指向iter#intersect#containsbysimilarHelpers指向core#slice#containsby。两者的差别在于输入形态lo.ContainsBy直接接收[]T切片it.ContainsBy接收惰性iter.Seq[T]当数据已经是切片且不需要惰性求值时lo.ContainsBy更直接当数据是 channel、无限生成器或需要链式Map/Filter时it.ContainsBy更合适。行为要点速查短路保证命中即返回谓词不会被调用超过到第一个命中元素为止的次数未命中或空集合时才会处理完整序列。空集合恒返回false与SomeBy注释中的语义一致与EveryBy空集合返回true形成对比。惰性友好因参数为iter.Seq[T]可安全用于it.Times、it.RepeatByit/seq.go#L208-L218生成的无限序列——只要存在命中遍历就会提前停止。无索引版本it包当前只提供ContainsBy没有带索引参数的变体同包的FilterI、MapI等提供了I后缀的带索引版本但存在性判断一族没有。若判断逻辑依赖元素位置可从序列侧用SeqToSeq2it/seq.go#L1170之类的辅助函数转换后再判断。环境要求it包受//go:build go1.23标签控制需 Go 1.23 编译仓库模块声明的最低版本是 Go 1.18go.mod低版本工具链下只有核心lo包可用。延伸阅读实现源码it/intersect.goContains、ContainsBy、EveryBy、SomeBy、NoneBy同文件成组实现单元测试it/intersect_test.goTestContainsBy的结构体与字符串两组子测试页面级索引docs/docs/iter/intersect.mdit包 intersect 分类辅助函数列表页切片版对照文档docs/data/core-containsby.mdlo.ContainsBy惰性序列构造参考it/seq.goTimes、RepeatBy、SeqToSeq2等【免费下载链接】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),仅供参考