aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--immutable.go37
-rw-r--r--immutable_test.go14
-rw-r--r--sets.go60
-rw-r--r--sets_test.go9
5 files changed, 41 insertions, 83 deletions
diff --git a/README.md b/README.md
index ed771c6..c547383 100644
--- a/README.md
+++ b/README.md
@@ -286,8 +286,8 @@ Please see the internal `defaultComparer` for an example, bearing in mind that i
## Set
-The `Set` represents a collection of unique values. It uses a `map[T]struct{}`, so it carries over some characteristics from the built-in Go `map` type.
-Values neeed to be `comparable`.
+The `Set` represents a collection of unique values, and it is implemented as a
+wrapper around a `Map[T, struct{}]`.
Like Maps, Sets require a `Hasher` to hash keys and check for equality. There are built-in
hasher implementations for most primitive types such as `int`, `uint`, and
diff --git a/immutable.go b/immutable.go
index b3d7dcb..666bb49 100644
--- a/immutable.go
+++ b/immutable.go
@@ -117,12 +117,8 @@ func (l *List[T]) set(index int, value T, mutable bool) *List[T] {
}
// Append returns a new list with value added to the end of the list.
-func (l *List[T]) Append(values ...T) *List[T] {
- other := l.clone()
- for _, value := range values {
- other.append(value, true)
- }
- return other
+func (l *List[T]) Append(value T) *List[T] {
+ return l.append(value, false)
}
func (l *List[T]) append(value T, mutable bool) *List[T] {
@@ -145,12 +141,8 @@ func (l *List[T]) append(value T, mutable bool) *List[T] {
}
// Prepend returns a new list with value(s) added to the beginning of the list.
-func (l *List[T]) Prepend(values ...T) *List[T] {
- other := l.clone()
- for i := len(values) - 1; i >= 0; i-- {
- other.prepend(values[i], true)
- }
- return other
+func (l *List[T]) Prepend(value T) *List[T] {
+ return l.prepend(value, false)
}
func (l *List[T]) prepend(value T, mutable bool) *List[T] {
@@ -752,18 +744,6 @@ func (m *Map[K, V]) Set(key K, value V) *Map[K, V] {
return m.set(key, value, false)
}
-// SetMany returns a map with the keys set to the new values. nil values are allowed.
-//
-// This function will return a new map even if the updated value is the same as
-// the existing value because Map does not track value equality.
-func (m *Map[K, V]) SetMany(entries map[K]V) *Map[K, V] {
- n := m.clone()
- for k, v := range entries {
- n.set(k, v, true)
- }
- return n
-}
-
func (m *Map[K, V]) set(key K, value V, mutable bool) *Map[K, V] {
// Set a hasher on the first value if one does not already exist.
hasher := m.hasher
@@ -1642,15 +1622,6 @@ func (m *SortedMap[K, V]) Set(key K, value V) *SortedMap[K, V] {
return m.set(key, value, false)
}
-// SetMany returns a map with the keys set to the new values.
-func (m *SortedMap[K, V]) SetMany(entries map[K]V) *SortedMap[K, V] {
- n := m.clone()
- for k, v := range entries {
- n.set(k, v, true)
- }
- return n
-}
-
func (m *SortedMap[K, V]) set(key K, value V, mutable bool) *SortedMap[K, V] {
// Set a comparer on the first value if one does not already exist.
comparer := m.comparer
diff --git a/immutable_test.go b/immutable_test.go
index a878a78..581d4d8 100644
--- a/immutable_test.go
+++ b/immutable_test.go
@@ -223,6 +223,19 @@ func TestList(t *testing.T) {
}
})
+ t.Run("AppendImmutable", func(t *testing.T) {
+ outer_l := NewList[int]()
+ for N := 0; N < 1_000; N++ {
+ l1 := outer_l.Append(0)
+ outer_l.Append(1)
+ if actual := l1.Get(N); actual != 0 {
+ t.Fatalf("Append mutates list with %d elements. Got %d instead of 0", N, actual)
+ }
+
+ outer_l = outer_l.Append(0)
+ }
+ })
+
RunRandom(t, "Random", func(t *testing.T, rand *rand.Rand) {
l := NewTList()
for i := 0; i < 100000; i++ {
@@ -2482,6 +2495,7 @@ func RunRandom(t *testing.T, name string, fn func(t *testing.T, rand *rand.Rand)
}
t.Run(name, func(t *testing.T) {
for i := 0; i < *randomN; i++ {
+ i := i
t.Run(fmt.Sprintf("%08d", i), func(t *testing.T) {
t.Parallel()
fn(t, rand.New(rand.NewSource(int64(i))))
diff --git a/sets.go b/sets.go
index fc30ffb..d610c15 100644
--- a/sets.go
+++ b/sets.go
@@ -14,37 +14,23 @@ type Set[T comparable] struct {
// Default hasher implementations only exist for int, string, and byte slice types.
// NewSet can also take some initial values as varargs.
func NewSet[T comparable](hasher Hasher[T], values ...T) Set[T] {
- s := Set[T]{
- m: NewMap[T, struct{}](hasher),
- }
+ m := NewMap[T, struct{}](hasher)
for _, value := range values {
- s.m.set(value, struct{}{}, true)
+ m = m.set(value, struct{}{}, true)
}
- return s
+ return Set[T]{m}
}
-// Set returns a set containing the new value.
+// Add returns a set containing the new value.
//
// This function will return a new set even if the set already contains the value.
-func (s Set[T]) Set(values ...T) Set[T] {
- n := Set[T]{
- m: s.m.clone(),
- }
- for _, value := range values {
- n.m.set(value, struct{}{}, true)
- }
- return n
+func (s Set[T]) Add(value T) Set[T] {
+ return Set[T]{s.m.Set(value, struct{}{})}
}
// Delete returns a set with the given key removed.
-func (s Set[T]) Delete(values ...T) Set[T] {
- n := Set[T]{
- m: s.m.clone(),
- }
- for _, value := range values {
- n.m.delete(value, true)
- }
- return n
+func (s Set[T]) Delete(value T) Set[T] {
+ return Set[T]{s.m.Delete(value)}
}
// Has returns true when the set contains the given value
@@ -133,37 +119,23 @@ type SortedSet[T comparable] struct {
// exist for int, string, and byte slice keys.
// NewSortedSet can also take some initial values as varargs.
func NewSortedSet[T comparable](comparer Comparer[T], values ...T) SortedSet[T] {
- s := SortedSet[T]{
- m: NewSortedMap[T, struct{}](comparer),
- }
+ m := NewSortedMap[T, struct{}](comparer)
for _, value := range values {
- s.m.set(value, struct{}{}, true)
+ m = m.set(value, struct{}{}, true)
}
- return s
+ return SortedSet[T]{m}
}
-// Set returns a set containing the new value.
+// Add returns a set containing the new value.
//
// This function will return a new set even if the set already contains the value.
-func (s SortedSet[T]) Set(values ...T) SortedSet[T] {
- n := SortedSet[T]{
- m: s.m.clone(),
- }
- for _, value := range values {
- n.m.set(value, struct{}{}, true)
- }
- return n
+func (s SortedSet[T]) Add(value T) SortedSet[T] {
+ return SortedSet[T]{s.m.Set(value, struct{}{})}
}
// Delete returns a set with the given key removed.
-func (s SortedSet[T]) Delete(values ...T) SortedSet[T] {
- n := SortedSet[T]{
- m: s.m.clone(),
- }
- for _, value := range values {
- n.m.delete(value, true)
- }
- return n
+func (s SortedSet[T]) Delete(value T) SortedSet[T] {
+ return SortedSet[T]{s.m.Delete(value)}
}
// Has returns true when the set contains the given value
diff --git a/sets_test.go b/sets_test.go
index b3900fc..ca3aa3d 100644
--- a/sets_test.go
+++ b/sets_test.go
@@ -6,7 +6,8 @@ import (
func TestSetsPut(t *testing.T) {
s := NewSet[string](nil)
- s2 := s.Set("1").Set("1")
+ s2 := s.Add("1").Add("1")
+ s2.Add("2")
if s.Len() != 0 {
t.Fatalf("Unexpected mutation of set")
}
@@ -33,7 +34,7 @@ func TestSetsPut(t *testing.T) {
func TestSetsDelete(t *testing.T) {
s := NewSet[string](nil)
- s2 := s.Set("1")
+ s2 := s.Add("1")
s3 := s.Delete("1")
if s2.Len() != 1 {
t.Fatalf("Unexpected non-mutation of set")
@@ -51,7 +52,7 @@ func TestSetsDelete(t *testing.T) {
func TestSortedSetsPut(t *testing.T) {
s := NewSortedSet[string](nil)
- s2 := s.Set("1").Set("1").Set("0")
+ s2 := s.Add("1").Add("1").Add("0")
if s.Len() != 0 {
t.Fatalf("Unexpected mutation of set")
}
@@ -85,7 +86,7 @@ func TestSortedSetsPut(t *testing.T) {
func TestSortedSetsDelete(t *testing.T) {
s := NewSortedSet[string](nil)
- s2 := s.Set("1")
+ s2 := s.Add("1")
s3 := s.Delete("1")
if s2.Len() != 1 {
t.Fatalf("Unexpected non-mutation of set")