Skip to content

Commit

Permalink
add advanced operations for set (#88)
Browse files Browse the repository at this point in the history
close #87
  • Loading branch information
chen3feng committed Sep 1, 2022
1 parent 90a2145 commit f431e70
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 23 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)的性能比较
Expand Down
85 changes: 80 additions & 5 deletions builtin_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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)
}
85 changes: 74 additions & 11 deletions builtin_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}

Expand All @@ -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("!"))
Expand All @@ -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")
Expand All @@ -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++
Expand All @@ -116,11 +120,70 @@ 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++
return false
})
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)))
}
Loading

0 comments on commit f431e70

Please sign in to comment.