diff --git a/go.mod b/go.mod index 07d0a13..d4318a0 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ module go-study -go 1.20 +go 1.23 require ( github.com/marusama/cyclicbarrier v1.1.0 github.com/stretchr/testify v1.9.0 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a - golang.org/x/sync v0.5.0 + go.uber.org/mock v0.5.0 + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/sync v0.9.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.uber.org/mock v0.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9594462..9951d05 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,26 @@ -github.com/brildum/testify v0.0.0-20151105045740-d05693e2e501 h1:ZWEmkmb/iJoRKLR0YuRP9wnEeF54NT3iUiGNTm/VQrs= -github.com/brildum/testify v0.0.0-20151105045740-d05693e2e501/go.mod h1:Zpjn5ClomJALLjz5sjBsNW79y3MarfpDcZaSJqbsIwk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/marusama/cyclicbarrier v1.1.0 h1:ol/AG+sjvh5yz832avbNjaowoerBuD3AgozxL+aD9u0= github.com/marusama/cyclicbarrier v1.1.0/go.mod h1:5u93l83cy51YXdz6eKq6kO9+9mGAooB6DHMAxcSuWwQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/iterator/set.go b/iterator/set.go new file mode 100644 index 0000000..e469724 --- /dev/null +++ b/iterator/set.go @@ -0,0 +1,52 @@ +package iterator + +import ( + "iter" +) + +type Set[T comparable] struct { + ordered []T // 用于保持顺序 + m map[T]struct{} +} + +func NewSet[T comparable]() *Set[T] { + return &Set[T]{make([]T, 0), make(map[T]struct{})} +} + +func (s *Set[T]) Add(e T) { + if !s.Contains(e) { + s.ordered = append(s.ordered, e) + } + s.m[e] = struct{}{} +} + +func (s *Set[T]) Remove(e T) { + for k, v := range s.ordered { + if v == e { + s.ordered = append(s.ordered[:k], s.ordered[k+1:]...) + break + } + } + + delete(s.m, e) +} + +func (s *Set[T]) Contains(e T) bool { + _, ok := s.m[e] + return ok +} + +func (s *Set[T]) Len() int { + return len(s.m) +} + +func (s *Set[T]) All() iter.Seq[T] { + return func(yield func(T) bool) { + // 迭代有序切片 + for _, v := range s.ordered { + if !yield(v) { + return + } + } + } +} diff --git a/iterator/set_test.go b/iterator/set_test.go new file mode 100644 index 0000000..a3ba3b8 --- /dev/null +++ b/iterator/set_test.go @@ -0,0 +1,48 @@ +package iterator + +import ( + "github.com/stretchr/testify/assert" + "iter" + "slices" + "testing" +) + +func TestSet(t *testing.T) { + s := NewSet[string]() + list := []string{ + "fantasticbin", + "phper", + "gopher", + "javaer", + } + var pull, push []string + expected := list[:3] + for v := range slices.Values(list) { + s.Add(v) + } + + // 只推 + for v := range s.All() { + if v == "javaer" { + break + } + push = append(push, v) + } + + // 既推又拉 + next, stop := iter.Pull(s.All()) + for { + v, ok := next() + if !ok { + break + } + + pull = append(pull, v) + if v == "gopher" { + stop() + } + } + + assert.Equal(t, expected, push) + assert.Equal(t, expected, pull) +}