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")