diff --git a/README.md b/README.md index 30813a7..c30c6c1 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ so C++ users may feel familiar, and sometimes (maybe) feel more convenient. Currently implemented containers are: -- [x] `BuiltinSet` provided a set funtionality based on Go's own `map` +- [x] `BuiltinSet` provided a set funtionality based on Go's own `map`. It provides basic operations such as insert, + search and remove, as well as advanced functions such as union, intersection, difference, subset, superset, and disjoint. - [x] `Vector` is a thin encapsulation based on `slice`. It provides functions such as insertion and deletion in the middle, range deletion, etc., and is still compatible with slices. - [x] `DList` is a doubly linked list. diff --git a/README_zh.md b/README_zh.md index 5476a78..23e9fcb 100644 --- a/README_zh.md +++ b/README_zh.md @@ -32,7 +32,7 @@ import "github.com/chen3feng/stl4go" 目前实现的容器有: -- [x] `Set` 集合。用 Go 自己的 map 封装了一个 `BuiltinSet` +- [x] `Set` 集合。用 Go 自己的 map 封装了一个 `BuiltinSet`,提供了插入查找删除等基本操作,以及并集、交集、差集、子集、超集、不交集等高级功能。 - [x] `Vector` 是基于 slice 封装的向量。提供了中间插入删除、区间删除等功能,依然与 slice 兼容。 - [x] `DList` 是双链表 - [x] [跳表(SkipList)](skiplist.md) 是一种有序的关联容器,可以填补 Go `map` 只支持无序的的空白。这是目前全 GitHub 最快的跳表,参见 [skiplist-survey](https://github.com/chen3feng/skiplist-survey)的性能比较 diff --git a/builtin_set.go b/builtin_set.go index af43827..2f1164f 100644 --- a/builtin_set.go +++ b/builtin_set.go @@ -4,7 +4,7 @@ import ( "fmt" ) -// BuiltinSet is an associative container that contains a unordered set of unique objects of type K. +// BuiltinSet is an associative container that contains an unordered set of unique objects of type K. type BuiltinSet[K comparable] map[K]struct{} // MakeBuiltinSetOf creates a new BuiltinSet object with the initial content from ks. @@ -25,9 +25,9 @@ func (s BuiltinSet[K]) Len() int { } // Clear implements the Container interface. -func (s *BuiltinSet[K]) Clear() { - for k := range *s { - delete(*s, k) +func (s BuiltinSet[K]) Clear() { + for k := range s { + delete(s, k) } } @@ -88,7 +88,82 @@ func (s BuiltinSet[K]) ForEachIf(cb func(k K) bool) { } } -// String implements the fmt.Stringer interface +// String implements the fmt.Stringer interface. func (s BuiltinSet[K]) String() string { return fmt.Sprintf("BuiltinSet[%s]%v", nameOfType[K](), s.Keys()) } + +// Update adds all elements from other to set. set |= other. +func (s BuiltinSet[K]) Update(other BuiltinSet[K]) { + for k := range other { + s[k] = struct{}{} + } +} + +// Union returns a new set with elements from the set and other. +func (s BuiltinSet[K]) Union(other BuiltinSet[K]) BuiltinSet[K] { + result := BuiltinSet[K]{} + result.Update(s) + result.Update(other) + return result +} + +func orderSet[K comparable](a, b BuiltinSet[K]) (small, large BuiltinSet[K]) { + if a.Len() < b.Len() { + return a, b + } + return b, a +} + +// Intersection returns a new set with elements common to the set and other. +func (s BuiltinSet[K]) Intersection(other BuiltinSet[K]) BuiltinSet[K] { + result := BuiltinSet[K]{} + small, large := orderSet(s, other) + for k := range small { + if large.Has(k) { + result.Insert(k) + } + } + return result +} + +// Difference returns a new set with elements in the set that are not in other. +func (s BuiltinSet[K]) Difference(other BuiltinSet[K]) BuiltinSet[K] { + result := BuiltinSet[K]{} + for k := range s { + if !other.Has(k) { + result.Insert(k) + } + } + return result +} + +// IsDisjointOf return True if the set has no elements in common with other. +// Sets are disjoint if and only if their intersection is the empty set. +func (s BuiltinSet[K]) IsDisjointOf(other BuiltinSet[K]) bool { + small, large := orderSet(s, other) + for k := range small { + if large.Has(k) { + return false + } + } + return true +} + +// IsSubsetOf tests whether every element in the set is in other. +func (s BuiltinSet[K]) IsSubsetOf(other BuiltinSet[K]) bool { + if s.Len() > other.Len() { + return false + } + for k := range s { + if !other.Has(k) { + return false + } + } + return true +} + +// IsSupersetOf tests whether every element in other is in the set. +func (s BuiltinSet[K]) IsSupersetOf(other BuiltinSet[K]) bool { + return other.IsSubsetOf(s) +} diff --git a/builtin_set_test.go b/builtin_set_test.go index 53f29a2..fd132f3 100644 --- a/builtin_set_test.go +++ b/builtin_set_test.go @@ -6,6 +6,10 @@ import ( "testing" ) +func setOf[T comparable](a ...T) BuiltinSet[T] { + return MakeBuiltinSetOf(a...) +} + func Test_BuiltinSet_Interface(t *testing.T) { s := make(BuiltinSet[int]) _ = Set[int](&s) @@ -24,7 +28,7 @@ func Test_MakeBuiltinSet2(t *testing.T) { } func Test_MakeBuiltinSetOf(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") expectEq(t, s.Len(), 2) } @@ -36,25 +40,25 @@ func Test_BuiltinSet_IsEmpty(t *testing.T) { } func Test_BuiltinSet_Clear(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") s.Clear() expectTrue(t, s.IsEmpty()) } func Test_BuiltinSet_String(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") expectTrue(t, strings.HasPrefix(fmt.Sprintf("%v", s), "BuiltinSet[string]")) } func Test_BuiltinSet_Has(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") expectTrue(t, s.Has("hello")) expectTrue(t, s.Has("world")) expectFalse(t, s.Has("!")) } func Test_BuiltinSet_Get(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") expectTrue(t, s.Has("hello")) expectTrue(t, s.Has("world")) expectFalse(t, s.Has("!")) @@ -77,7 +81,7 @@ func Test_BuiltinSet_InsertN(t *testing.T) { } func Test_BuiltinSet_Remove(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") s.Remove("hello") expectEq(t, s.Len(), 1) s.Remove("hello") @@ -87,27 +91,27 @@ func Test_BuiltinSet_Remove(t *testing.T) { } func Test_BuiltinSet_RemoveN(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") s.RemoveN("hello", "world") s.Remove("world") expectTrue(t, s.IsEmpty()) } func Test_BuiltinSet_Keys(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") ks := s.Keys() expectEq(t, 2, len(ks)) } func Test_BuiltinSet_For(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") for v := range s { expectTrue(t, v == "hello" || v == "world") } } func Test_BuiltinSet_ForEach(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") c := 0 s.ForEach(func(string) { c++ @@ -116,7 +120,7 @@ func Test_BuiltinSet_ForEach(t *testing.T) { } func Test_BuiltinSet_ForEachIf(t *testing.T) { - s := MakeBuiltinSetOf("hello", "world") + s := setOf("hello", "world") c := 0 s.ForEachIf(func(string) bool { c++ @@ -124,3 +128,62 @@ func Test_BuiltinSet_ForEachIf(t *testing.T) { }) expectLt(t, c, 2) } + +func Test_BuiltinSet_Update(t *testing.T) { + s := setOf(1, 2, 3) + s.Update(setOf(3, 4)) + expectEq(t, s.Len(), 4) + expectTrue(t, s.Has(4)) +} + +func Test_BuiltinSet_Union(t *testing.T) { + s := setOf(1, 2, 3) + s2 := s.Union(setOf(3, 4)) + expectEq(t, s2.Len(), 4) + expectTrue(t, s2.Has(4)) +} + +func Test_BuiltinSet_Intersection(t *testing.T) { + s := setOf(1, 2, 3).Intersection(setOf(3, 4)) + expectEq(t, s.Len(), 1) + expectTrue(t, s.Has(3)) + s = setOf(3, 4).Intersection(setOf(1, 2, 3)) + expectEq(t, s.Len(), 1) + expectTrue(t, s.Has(3)) +} + +func Test_BuiltinSet_Difference(t *testing.T) { + s := setOf(1, 2, 3).Difference(setOf(3, 4)) + expectEq(t, s.Len(), 2) + expectTrue(t, s.Has(1)) + expectTrue(t, s.Has(2)) + s = setOf(1, 2).Difference(setOf(3, 4)) + expectEq(t, s.Len(), 2) + expectTrue(t, s.Has(1)) + expectTrue(t, s.Has(2)) +} + +func Test_BuiltinSet_IsDisjointOf(t *testing.T) { + s1 := setOf(1, 2, 3) + s2 := setOf(3, 4) + expectFalse(t, s1.IsDisjointOf(s2)) + expectTrue(t, s1.IsDisjointOf(setOf(4, 5))) +} + +func Test_BuiltinSet_IsSubsetOf(t *testing.T) { + expectTrue(t, setOf[int]().IsSubsetOf(setOf[int]())) + expectTrue(t, setOf[int]().IsSubsetOf(setOf(1))) + expectTrue(t, setOf(1, 2, 3).IsSubsetOf(setOf(1, 2, 3))) + expectTrue(t, setOf(1, 2).IsSubsetOf(setOf(1, 2, 3))) + expectFalse(t, setOf(1, 2, 3).IsSubsetOf(setOf(1, 2))) + expectFalse(t, setOf(1, 2).IsSubsetOf(setOf(2, 3))) +} + +func Test_BuiltinSet_IsSupersetOf(t *testing.T) { + expectTrue(t, setOf[int]().IsSupersetOf(setOf[int]())) + expectTrue(t, setOf(1).IsSupersetOf(setOf[int]())) + expectTrue(t, setOf(1, 2, 3).IsSupersetOf(setOf(1, 2, 3))) + expectTrue(t, setOf(1, 2, 3).IsSupersetOf(setOf(1, 2))) + expectFalse(t, setOf(1, 2).IsSupersetOf(setOf(1, 2, 3))) + expectFalse(t, setOf(1, 2).IsSupersetOf(setOf(2, 3))) +} diff --git a/generated_doc.md b/generated_doc.md index 20eadba..2103a32 100755 --- a/generated_doc.md +++ b/generated_doc.md @@ -77,18 +77,25 @@ Package stl4go is a generic container and algorithm library for go. - [func UpperBoundFunc[T any](a []T, value T, less LessFn[T]) int](<#func-upperboundfunc>) - [type BuiltinSet](<#type-builtinset>) - [func MakeBuiltinSetOf[K comparable](ks ...K) BuiltinSet[K]](<#func-makebuiltinsetof>) - - [func (s *BuiltinSet[K]) Clear()](<#func-builtinsetk-clear>) + - [func (s BuiltinSet[K]) Clear()](<#func-builtinsetk-clear>) + - [func (s BuiltinSet[K]) Difference(other BuiltinSet[K]) BuiltinSet[K]](<#func-builtinsetk-difference>) - [func (s BuiltinSet[K]) ForEach(cb func(k K))](<#func-builtinsetk-foreach>) - [func (s BuiltinSet[K]) ForEachIf(cb func(k K) bool)](<#func-builtinsetk-foreachif>) - [func (s BuiltinSet[K]) Has(k K) bool](<#func-builtinsetk-has>) - [func (s BuiltinSet[K]) Insert(k K)](<#func-builtinsetk-insert>) - [func (s BuiltinSet[K]) InsertN(ks ...K)](<#func-builtinsetk-insertn>) + - [func (s BuiltinSet[K]) Intersection(other BuiltinSet[K]) BuiltinSet[K]](<#func-builtinsetk-intersection>) + - [func (s BuiltinSet[K]) IsDisjointOf(other BuiltinSet[K]) bool](<#func-builtinsetk-isdisjointof>) - [func (s BuiltinSet[K]) IsEmpty() bool](<#func-builtinsetk-isempty>) + - [func (s BuiltinSet[K]) IsSubsetOf(other BuiltinSet[K]) bool](<#func-builtinsetk-issubsetof>) + - [func (s BuiltinSet[K]) IsSupersetOf(other BuiltinSet[K]) bool](<#func-builtinsetk-issupersetof>) - [func (s BuiltinSet[K]) Keys() []K](<#func-builtinsetk-keys>) - [func (s BuiltinSet[K]) Len() int](<#func-builtinsetk-len>) - [func (s BuiltinSet[K]) Remove(k K) bool](<#func-builtinsetk-remove>) - [func (s BuiltinSet[K]) RemoveN(ks ...K)](<#func-builtinsetk-removen>) - [func (s BuiltinSet[K]) String() string](<#func-builtinsetk-string>) + - [func (s BuiltinSet[K]) Union(other BuiltinSet[K]) BuiltinSet[K]](<#func-builtinsetk-union>) + - [func (s BuiltinSet[K]) Update(other BuiltinSet[K])](<#func-builtinsetk-update>) - [type CompareFn](<#type-comparefn>) - [type Container](<#type-container>) - [type DList](<#type-dlist>) @@ -817,7 +824,7 @@ Complexity: O\(log\(len\(a\)\)\). ## type [BuiltinSet]() -BuiltinSet is an associative container that contains a unordered set of unique objects of type K. +BuiltinSet is an associative container that contains an unordered set of unique objects of type K. ```go type BuiltinSet[K comparable] map[K]struct{} @@ -831,14 +838,22 @@ func MakeBuiltinSetOf[K comparable](ks ...K) BuiltinSet[K] MakeBuiltinSetOf creates a new BuiltinSet object with the initial content from ks. -### func \(\*BuiltinSet\[K\]\) [Clear]() +### func \(BuiltinSet\[K\]\) [Clear]() ```go -func (s *BuiltinSet[K]) Clear() +func (s BuiltinSet[K]) Clear() ``` Clear implements the Container interface. +### func \(BuiltinSet\[K\]\) [Difference]() + +```go +func (s BuiltinSet[K]) Difference(other BuiltinSet[K]) BuiltinSet[K] +``` + +Difference returns a new set with elements in the set that are not in other. + ### func \(BuiltinSet\[K\]\) [ForEach]() ```go @@ -879,6 +894,22 @@ func (s BuiltinSet[K]) InsertN(ks ...K) InsertN implements the Set interface. +### func \(BuiltinSet\[K\]\) [Intersection]() + +```go +func (s BuiltinSet[K]) Intersection(other BuiltinSet[K]) BuiltinSet[K] +``` + +Intersection returns a new set with elements common to the set and other. + +### func \(BuiltinSet\[K\]\) [IsDisjointOf]() + +```go +func (s BuiltinSet[K]) IsDisjointOf(other BuiltinSet[K]) bool +``` + +IsDisjointOf return True if the set has no elements in common with other. Sets are disjoint if and only if their intersection is the empty set. + ### func \(BuiltinSet\[K\]\) [IsEmpty]() ```go @@ -887,6 +918,22 @@ func (s BuiltinSet[K]) IsEmpty() bool IsEmpty implements the Container interface. +### func \(BuiltinSet\[K\]\) [IsSubsetOf]() + +```go +func (s BuiltinSet[K]) IsSubsetOf(other BuiltinSet[K]) bool +``` + +IsSubsetOf tests whether every element in the set is in other. + +### func \(BuiltinSet\[K\]\) [IsSupersetOf]() + +```go +func (s BuiltinSet[K]) IsSupersetOf(other BuiltinSet[K]) bool +``` + +IsSupersetOf tests whether every element in other is in the set. + ### func \(BuiltinSet\[K\]\) [Keys]() ```go @@ -925,7 +972,23 @@ RemoveN implements the Set interface. func (s BuiltinSet[K]) String() string ``` -String implements the fmt.Stringer interface +String implements the fmt.Stringer interface. + +### func \(BuiltinSet\[K\]\) [Union]() + +```go +func (s BuiltinSet[K]) Union(other BuiltinSet[K]) BuiltinSet[K] +``` + +Union returns a new set with elements from the set and other. + +### func \(BuiltinSet\[K\]\) [Update]() + +```go +func (s BuiltinSet[K]) Update(other BuiltinSet[K]) +``` + +Update adds all elements from other to set. set |= other. ## type [CompareFn]()