Just a commit
This commit is contained in:
@@ -1 +1 @@
|
||||
react
|
||||
custom-set
|
24
go/custom-set/README.md
Normal file
24
go/custom-set/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Custom Set
|
||||
|
||||
Create a custom set type.
|
||||
|
||||
Sometimes it is necessary to define a custom data structure of some
|
||||
type, like a set. In this exercise you will define your own set. How it
|
||||
works internally doesn't matter, as long as it behaves like a set of
|
||||
unique elements.
|
||||
|
||||
To run the tests simply run the command `go test` in the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||
flag:
|
||||
|
||||
go test -bench .
|
||||
|
||||
For more detailed info about the Go track see the [help
|
||||
page](http://exercism.io/languages/go).
|
||||
|
||||
|
||||
|
||||
## Submitting Incomplete Problems
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||
|
368
go/custom-set/cases_test.go
Normal file
368
go/custom-set/cases_test.go
Normal file
@@ -0,0 +1,368 @@
|
||||
package stringset
|
||||
|
||||
// Source: exercism/x-common
|
||||
// Commit: 269f498 Merge pull request #48 from soniakeys/custom-set-json
|
||||
|
||||
// Test two sets for equality.
|
||||
var eqCases = []binBoolCase{
|
||||
{ // order doesn't matter
|
||||
[]string{"a", "c"},
|
||||
[]string{"c", "a"},
|
||||
true,
|
||||
},
|
||||
{ // dupicates don't matter
|
||||
[]string{"a", "a"},
|
||||
[]string{"a"},
|
||||
true,
|
||||
},
|
||||
{ // empty sets are equal
|
||||
[]string{},
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{ // set with single element is equal to itself
|
||||
[]string{"a"},
|
||||
[]string{"a"},
|
||||
true,
|
||||
},
|
||||
{ // different sets are not equal
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"c", "d", "e"},
|
||||
false,
|
||||
},
|
||||
{ // empty set is not equal to non-empty set
|
||||
[]string{},
|
||||
[]string{"a", "b", "c"},
|
||||
false,
|
||||
},
|
||||
{ // non-empty set is not equal to empty set
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{ // having most in common is not good enough
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{"b", "c", "d", "e"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// Add an element to a set.
|
||||
var addCases = []eleOpCase{
|
||||
{ // add to empty set
|
||||
[]string{},
|
||||
"c",
|
||||
[]string{"c"},
|
||||
},
|
||||
{ // add to non-empty set
|
||||
[]string{"a", "b", "d"},
|
||||
"c",
|
||||
[]string{"a", "b", "c", "d"},
|
||||
},
|
||||
{ // add existing element
|
||||
[]string{"a", "b", "c"},
|
||||
"c",
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
}
|
||||
|
||||
// Delete an element from a set.
|
||||
var delCases = []eleOpCase{
|
||||
{ // delete an element
|
||||
[]string{"c", "b", "a"},
|
||||
"b",
|
||||
[]string{"a", "c"},
|
||||
},
|
||||
{ // delete an element not in set
|
||||
[]string{"c", "b", "a"},
|
||||
"d",
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
}
|
||||
|
||||
// Test if is a set is empty.
|
||||
var emptyCases = []unaryBoolCase{
|
||||
{ // empty
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{ // single element
|
||||
[]string{"a"},
|
||||
false,
|
||||
},
|
||||
{ // a few elements
|
||||
[]string{"a", "b", "c", "b"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// Return the cardinality of a set.
|
||||
var lenCases = []unaryIntCase{
|
||||
{ // empty set
|
||||
[]string{},
|
||||
0,
|
||||
},
|
||||
{ // non-empty set
|
||||
[]string{"a", "b", "c"},
|
||||
3,
|
||||
},
|
||||
{ // duplicate element
|
||||
[]string{"a", "b", "c", "b"},
|
||||
3,
|
||||
},
|
||||
}
|
||||
|
||||
// Test if a value is an element of a set.
|
||||
var hasCases = []eleBoolCase{
|
||||
{ // nothing is an element of the empty set
|
||||
[]string{},
|
||||
"a",
|
||||
false,
|
||||
},
|
||||
{ // 1 is in the set
|
||||
[]string{"a", "b", "c", "b"},
|
||||
"a",
|
||||
true,
|
||||
},
|
||||
{ // 2 is in the set
|
||||
[]string{"a", "b", "c", "b"},
|
||||
"b",
|
||||
true,
|
||||
},
|
||||
{ // 3 is in the set
|
||||
[]string{"a", "b", "c", "b"},
|
||||
"c",
|
||||
true,
|
||||
},
|
||||
{ // 4 not in the set
|
||||
[]string{"a", "b", "c", "b"},
|
||||
"d",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// Test if set1 is a subset of set2.
|
||||
var subsetCases = []binBoolCase{
|
||||
{ // empty set is subset of itself
|
||||
[]string{},
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{ // empty set is subset of non-empty set
|
||||
[]string{},
|
||||
[]string{"a"},
|
||||
true,
|
||||
},
|
||||
{ // non-empty set is not subset of empty set
|
||||
[]string{"a"},
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{ // non-empty set is subset of itself
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"a", "b", "c"},
|
||||
true,
|
||||
},
|
||||
{ // proper subset
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"d", "a", "b", "c"},
|
||||
true,
|
||||
},
|
||||
{ // same number of elements
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"d", "a", "c"},
|
||||
false,
|
||||
},
|
||||
{ // superset
|
||||
[]string{"a", "b", "c", "d", "e"},
|
||||
[]string{"b", "c", "d"},
|
||||
false,
|
||||
},
|
||||
{ // fewer elements but not a subset
|
||||
[]string{"a", "b", "c", "k"},
|
||||
[]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// Test if two sets are disjoint.
|
||||
var disjointCases = []binBoolCase{
|
||||
{ // the empty set is disjoint with itself
|
||||
[]string{},
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{ // empty set disjoint with non-empty set
|
||||
[]string{},
|
||||
[]string{"a"},
|
||||
true,
|
||||
},
|
||||
{ // non-empty set disjoint with empty set
|
||||
[]string{"a"},
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{ // one element in common
|
||||
[]string{"a", "b"},
|
||||
[]string{"b", "c"},
|
||||
false,
|
||||
},
|
||||
{ // no elements in common
|
||||
[]string{"a", "b"},
|
||||
[]string{"c", "d"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
// Produce the union of two sets.
|
||||
var unionCases = []binOpCase{
|
||||
{ // union of empty sets
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{ // union of empty set with set of one element
|
||||
[]string{},
|
||||
[]string{"b"},
|
||||
[]string{"b"},
|
||||
},
|
||||
{ // union of empty set with non-empty set
|
||||
[]string{},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{"b", "c", "e"},
|
||||
},
|
||||
{ // union of non-empty set with empty set
|
||||
[]string{"a", "c"},
|
||||
[]string{},
|
||||
[]string{"a", "c"},
|
||||
},
|
||||
{ // union of a set with itself
|
||||
[]string{"a", "c"},
|
||||
[]string{"c", "a"},
|
||||
[]string{"a", "c"},
|
||||
},
|
||||
{ // union with one element
|
||||
[]string{"a", "c"},
|
||||
[]string{"b"},
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
{ // one element in common, one different
|
||||
[]string{"a", "c"},
|
||||
[]string{"b", "c"},
|
||||
[]string{"c", "b", "a"},
|
||||
},
|
||||
{ // two elements in common
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{"a", "b", "c", "d", "e"},
|
||||
},
|
||||
}
|
||||
|
||||
// Intersect two sets.
|
||||
var intersectionCases = []binOpCase{
|
||||
{ // intersect empty sets
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{ // intersect empty set with non-empty set
|
||||
[]string{},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{},
|
||||
},
|
||||
{ // intersect non-empty set with empty set
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{ // intersect one element with itself
|
||||
[]string{"c"},
|
||||
[]string{"c"},
|
||||
[]string{"c"},
|
||||
},
|
||||
{ // one element in common, extra elements in both sets
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"c", "e", "d"},
|
||||
[]string{"c"},
|
||||
},
|
||||
{ // two elements in common, extras in both sets
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{"b", "c"},
|
||||
},
|
||||
{ // intersect with subset
|
||||
[]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
|
||||
[]string{"e", "f", "g", "h", "i", "j"},
|
||||
[]string{"e", "f", "g", "h", "i", "j"},
|
||||
},
|
||||
{ // nothing in common
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"d", "e", "f"},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
// Produce the set difference (set1 - set2)
|
||||
// or more specifically, (set1 ∖ set2)
|
||||
var differenceCases = []binOpCase{
|
||||
{ // difference of two empty sets
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{ // difference of empty set and non-empty set
|
||||
[]string{},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{},
|
||||
},
|
||||
{ // difference of non-empty set and empty set
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
},
|
||||
{ // no elements in common
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"d"},
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
{ // one element in common, one extra
|
||||
[]string{"c", "b", "a"},
|
||||
[]string{"b", "d"},
|
||||
[]string{"a", "c"},
|
||||
},
|
||||
{ // two elements in common, one extra
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{"a", "d"},
|
||||
},
|
||||
}
|
||||
|
||||
// Produce the symmetric difference of two sets. The symmetric
|
||||
// difference consists of elements in one or the other but not both.
|
||||
var symmetricDifferenceCases = []binOpCase{
|
||||
{ // two empty sets
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{ // empty set and non-empty set
|
||||
[]string{},
|
||||
[]string{"c", "b", "e"},
|
||||
[]string{"c", "b", "e"},
|
||||
},
|
||||
{ // non-empty set and empty set
|
||||
[]string{"a", "b", "c", "d"},
|
||||
[]string{},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
},
|
||||
{ // no elements in common
|
||||
[]string{"a", "b", "c"},
|
||||
[]string{"d"},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
},
|
||||
{ // one element in common
|
||||
[]string{"c", "b", "a"},
|
||||
[]string{"b", "d"},
|
||||
[]string{"a", "c", "d"},
|
||||
},
|
||||
}
|
BIN
go/custom-set/cmd/cmd
Executable file
BIN
go/custom-set/cmd/cmd
Executable file
Binary file not shown.
49
go/custom-set/cmd/main.go
Normal file
49
go/custom-set/cmd/main.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"../../custom-set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Creating Set 1")
|
||||
s1 := stringset.NewFromSlice([]string{"a", "b"})
|
||||
addAndOutput(s1, "A")
|
||||
addAndOutput(s1, "c")
|
||||
addAndOutput(s1, "B")
|
||||
addAndOutput(s1, "B1")
|
||||
addAndOutput(s1, "A1")
|
||||
addAndOutput(s1, "B2")
|
||||
addAndOutput(s1, "A2")
|
||||
addAndOutput(s1, "B3")
|
||||
addAndOutput(s1, "A3")
|
||||
delAndOutput(s1, "a")
|
||||
|
||||
fmt.Println("Creating Set 2")
|
||||
s2 := stringset.NewFromSlice([]string{"A", "c"})
|
||||
addAndOutput(s2, "a")
|
||||
addAndOutput(s2, "b")
|
||||
addAndOutput(s2, "B1")
|
||||
addAndOutput(s2, "A3")
|
||||
addAndOutput(s2, "A2")
|
||||
addAndOutput(s2, "B2")
|
||||
addAndOutput(s2, "A1")
|
||||
addAndOutput(s2, "B3")
|
||||
addAndOutput(s2, "B")
|
||||
s2.PrettyPrint()
|
||||
delAndOutput(s2, "a")
|
||||
s2.PrettyPrint()
|
||||
}
|
||||
|
||||
func addAndOutput(s *stringset.Set, val string) {
|
||||
fmt.Println("Adding " + val)
|
||||
s.Add(val)
|
||||
fmt.Println(s.String())
|
||||
}
|
||||
|
||||
func delAndOutput(s *stringset.Set, val string) {
|
||||
fmt.Println("Deleting " + val)
|
||||
s.Delete(val)
|
||||
fmt.Println(s.String())
|
||||
}
|
268
go/custom-set/custom_set_test.go
Normal file
268
go/custom-set/custom_set_test.go
Normal file
@@ -0,0 +1,268 @@
|
||||
package stringset
|
||||
|
||||
// Implement Set as a collection of unique string values.
|
||||
//
|
||||
// API:
|
||||
//
|
||||
// New() Set
|
||||
// NewFromSlice([]string) Set
|
||||
// (s Set) Add(string) // modify s
|
||||
// (s Set) Delete(string) // modify s
|
||||
// (s Set) Has(string) bool
|
||||
// (s Set) IsEmpty() bool
|
||||
// (s Set) Len() int
|
||||
// (s Set) Slice() []string
|
||||
// (s Set) String() string
|
||||
// Equal(s1, s2 Set) bool
|
||||
// Subset(s1, s2 Set) bool // return s1 ⊆ s2
|
||||
// Disjoint(s1, s2 Set) bool
|
||||
// Intersection(s1, s2 Set) Set
|
||||
// Union(s1, s2 Set) Set
|
||||
// Difference(s1, s2 Set) Set // return s1 ∖ s2
|
||||
// SymmetricDifference(s1, s2 Set) Set
|
||||
//
|
||||
// For Set.String, use '{' and '}', output elements as double-quoted strings
|
||||
// safely escaped with Go syntax, and use a comma and a single space between
|
||||
// elements. For example {"a", "b"}.
|
||||
// Format the empty set as {}.
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const targetTestVersion = 3
|
||||
|
||||
func TestTestVersion(t *testing.T) {
|
||||
if testVersion != targetTestVersion {
|
||||
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// A first set of tests uses Set.String() to judge correctness.
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
// New must return an empty set.
|
||||
want := "{}"
|
||||
if got := New().String(); got != want {
|
||||
t.Fatalf(`New().String() = %s, want %s.`, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFromSlice(t *testing.T) {
|
||||
// nil slice should give empty set
|
||||
want := "{}"
|
||||
if got := NewFromSlice(nil).String(); got != want {
|
||||
t.Fatalf(`NewFromSlice(nil) = %s, want %s.`, got, want)
|
||||
}
|
||||
|
||||
// slice with one element:
|
||||
want = `{"a"}`
|
||||
if got := NewFromSlice([]string{"a"}).String(); got != want {
|
||||
t.Fatalf(`NewFromSlice([]string{"a"}) = %s, want %s.`, got, want)
|
||||
}
|
||||
|
||||
// slice with repeated element:
|
||||
if got := NewFromSlice([]string{"a", "a"}).String(); got != want {
|
||||
t.Fatalf(`NewFromSlice([]string{"a", "a"}) = %s, want %s.`, got, want)
|
||||
}
|
||||
|
||||
// slice with two elements:
|
||||
got := NewFromSlice([]string{"a", "b"}).String()
|
||||
want1 := `{"a", "b"}`
|
||||
want2 := `{"b", "a"}`
|
||||
if got != want1 && got != want2 { // order undefined
|
||||
t.Fatalf(`NewFromSlice([]string{"a", "b"}) = %s, want %s or (%s).`,
|
||||
got, want1, want2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
// empty set should produce empty slice
|
||||
s := New()
|
||||
if l := s.Slice(); len(l) != 0 {
|
||||
t.Fatalf(`s.Slice() = %q, want []`, l)
|
||||
}
|
||||
|
||||
// one element:
|
||||
want := []string{"a"}
|
||||
s = NewFromSlice(want)
|
||||
got := s.Slice()
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf(`%v Slice = %q, want %q`, s, got, want)
|
||||
}
|
||||
|
||||
// two elements:
|
||||
w1 := []string{"a", "b"}
|
||||
w2 := []string{"b", "a"}
|
||||
s = NewFromSlice(w1)
|
||||
got = s.Slice()
|
||||
if !reflect.DeepEqual(got, w1) && !reflect.DeepEqual(got, w2) {
|
||||
t.Fatalf(`%v Slice = %q, want %q`, s, got, w1)
|
||||
}
|
||||
}
|
||||
|
||||
// Trusting NewFromSlice now, remaining tests are table driven, taking data
|
||||
// from cases_test.go and building sets with NewFromSlice.
|
||||
|
||||
// test case types used in cases_test.go
|
||||
type (
|
||||
// binary function, bool result (Equal, Subset, Disjoint)
|
||||
binBoolCase struct {
|
||||
set1 []string
|
||||
set2 []string
|
||||
want bool
|
||||
}
|
||||
// unary function, bool result (IsEmpty)
|
||||
unaryBoolCase struct {
|
||||
set []string
|
||||
want bool
|
||||
}
|
||||
// unary function, int result (Len)
|
||||
unaryIntCase struct {
|
||||
set []string
|
||||
want int
|
||||
}
|
||||
// set-element function, bool result (Has)
|
||||
eleBoolCase struct {
|
||||
set []string
|
||||
ele string
|
||||
want bool
|
||||
}
|
||||
// set-element operator (Add, Delete)
|
||||
eleOpCase struct {
|
||||
set []string
|
||||
ele string
|
||||
want []string
|
||||
}
|
||||
// set-set operator (Union, Intersection, Difference, Symmetric-Difference)
|
||||
binOpCase struct {
|
||||
set1 []string
|
||||
set2 []string
|
||||
want []string
|
||||
}
|
||||
)
|
||||
|
||||
// helper for testing Equal, Subset, Disjoint
|
||||
func testBinBool(name string, f func(Set, Set) bool, cases []binBoolCase, t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
s1 := NewFromSlice(tc.set1)
|
||||
s2 := NewFromSlice(tc.set2)
|
||||
got := f(s1, s2)
|
||||
if got != tc.want {
|
||||
t.Fatalf("%s(%v, %v) = %t, want %t", name, s1, s2, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
testBinBool("Equal", Equal, eqCases, t)
|
||||
}
|
||||
|
||||
// With Equal tested, remaining tests use it to judge correctness.
|
||||
|
||||
// helper for testing Add, Delete
|
||||
func testEleOp(name string, op func(Set, string), cases []eleOpCase, t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
s := NewFromSlice(tc.set)
|
||||
op(s, tc.ele)
|
||||
want := NewFromSlice(tc.want)
|
||||
if !Equal(s, want) {
|
||||
t.Fatalf("%v %s %q = %v, want %v",
|
||||
NewFromSlice(tc.set), name, tc.ele, s, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
testEleOp("Add", Set.Add, addCases, t)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
testEleOp("Delete", Set.Delete, delCases, t)
|
||||
}
|
||||
|
||||
func TestHas(t *testing.T) {
|
||||
for _, tc := range hasCases {
|
||||
s := NewFromSlice(tc.set)
|
||||
got := s.Has(tc.ele)
|
||||
if got != tc.want {
|
||||
t.Fatalf("%v Has %q = %t, want %t", s, tc.ele, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
for _, tc := range emptyCases {
|
||||
s := NewFromSlice(tc.set)
|
||||
got := s.IsEmpty()
|
||||
if got != tc.want {
|
||||
t.Fatalf("%v IsEmpty = %t, want %t", s, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLen(t *testing.T) {
|
||||
for _, tc := range lenCases {
|
||||
s := NewFromSlice(tc.set)
|
||||
got := s.Len()
|
||||
if got != tc.want {
|
||||
t.Fatalf("%v Len = %d, want %d", s, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubset(t *testing.T) {
|
||||
testBinBool("Subset", Subset, subsetCases, t)
|
||||
}
|
||||
|
||||
func TestDisjoint(t *testing.T) {
|
||||
testBinBool("Disjoint", Disjoint, disjointCases, t)
|
||||
}
|
||||
|
||||
// helper for testing Union, Intersection, Difference, SymmetricDifference
|
||||
func testBinOp(name string, f func(Set, Set) Set, cases []binOpCase, t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
s1 := NewFromSlice(tc.set1)
|
||||
s2 := NewFromSlice(tc.set2)
|
||||
want := NewFromSlice(tc.want)
|
||||
got := f(s1, s2)
|
||||
if !Equal(got, want) {
|
||||
t.Fatalf("%s(%v, %v) = %v, want %v", name, s1, s2, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
testBinOp("Union", Union, unionCases, t)
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
testBinOp("Intersection", Intersection, intersectionCases, t)
|
||||
}
|
||||
|
||||
func TestDifference(t *testing.T) {
|
||||
testBinOp("Difference", Difference, differenceCases, t)
|
||||
}
|
||||
|
||||
func TestSymmetricDifference(t *testing.T) {
|
||||
testBinOp("SymmetricDifference", SymmetricDifference, symmetricDifferenceCases, t)
|
||||
}
|
||||
|
||||
func BenchmarkNewFromSlice1e1(b *testing.B) { bench(1e1, b) }
|
||||
func BenchmarkNewFromSlice1e2(b *testing.B) { bench(1e2, b) }
|
||||
func BenchmarkNewFromSlice1e3(b *testing.B) { bench(1e3, b) }
|
||||
func BenchmarkNewFromSlice1e4(b *testing.B) { bench(1e4, b) }
|
||||
|
||||
func bench(nAdd int, b *testing.B) {
|
||||
s := make([]string, nAdd)
|
||||
for i := range s {
|
||||
s[i] = strconv.Itoa(rand.Intn(len(s)))
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
NewFromSlice(s)
|
||||
}
|
||||
}
|
388
go/custom-set/stringset.go
Normal file
388
go/custom-set/stringset.go
Normal file
@@ -0,0 +1,388 @@
|
||||
package stringset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const testVersion = 3
|
||||
|
||||
// Set is a slice of strings that you can do set operations on.
|
||||
// I decided that I wanted to implement a binary tree for the storage
|
||||
type Set struct {
|
||||
top SetValue
|
||||
}
|
||||
|
||||
type SetValue struct {
|
||||
value string
|
||||
left *SetValue
|
||||
right *SetValue
|
||||
}
|
||||
|
||||
// New returns an empty Set
|
||||
func New() Set {
|
||||
s := new(Set)
|
||||
s.top = SetValue{}
|
||||
return *s
|
||||
}
|
||||
|
||||
// NewFromSlice takes a slice of strings and returns a Set
|
||||
func NewFromSlice(s []string) Set {
|
||||
ret := New()
|
||||
for i := range s {
|
||||
ret.Add(s[i])
|
||||
}
|
||||
ret.balance()
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add adds a value to the set
|
||||
func (s Set) Add(v string) {
|
||||
if s.top.value == "" {
|
||||
s.top.value = v
|
||||
return
|
||||
}
|
||||
s.top.Add(v)
|
||||
}
|
||||
|
||||
func (s *Set) PrettyPrint() {
|
||||
s.pp(&s.top, 0)
|
||||
}
|
||||
|
||||
func (s *Set) pp(n *SetValue, indent int) {
|
||||
if n != nil {
|
||||
if n.left != nil {
|
||||
s.pp(n.left, indent+4)
|
||||
}
|
||||
if n.right != nil {
|
||||
s.pp(n.right, indent+4)
|
||||
}
|
||||
for ; indent > 0; indent-- {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
fmt.Println(n.value)
|
||||
}
|
||||
}
|
||||
|
||||
// balance balances the binary tree
|
||||
func (s *Set) balance() {
|
||||
|
||||
}
|
||||
|
||||
func (s *Set) find(v string) *SetValue {
|
||||
if s.top.value != "" {
|
||||
sv, _ := s.top.find(v)
|
||||
return sv
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findWithParent finds a node with a child value v
|
||||
// a return of nil, nil means it's the top node
|
||||
func (s *Set) findParent(v string) (*SetValue, error) {
|
||||
if s.top.value == v {
|
||||
// no parent, it's the top.
|
||||
return nil, nil
|
||||
} else {
|
||||
return s.top.findParent(v)
|
||||
}
|
||||
return nil, errors.New("Empty Set")
|
||||
}
|
||||
|
||||
func (s *Set) findHome(v *SetValue) {
|
||||
if s.top.value == "" {
|
||||
s.top.value = v.value
|
||||
s.top.left = v.left
|
||||
s.top.right = v.right
|
||||
return
|
||||
}
|
||||
s.top.findHome(v)
|
||||
}
|
||||
|
||||
// Delete removes the given value from the set
|
||||
func (s Set) Delete(v string) {
|
||||
if sv, err := s.findParent(v); err == nil {
|
||||
var cmp int
|
||||
var delNode, repNode, orphan *SetValue
|
||||
if sv == nil {
|
||||
// Deleting 'top'
|
||||
delNode = &s.top
|
||||
if delNode.left != nil {
|
||||
repNode = delNode.left
|
||||
orphan = delNode.right
|
||||
} else if delNode.right != nil {
|
||||
repNode = delNode.right
|
||||
}
|
||||
if repNode == nil {
|
||||
// No node to replace it with, we're done
|
||||
return
|
||||
}
|
||||
s.top = *repNode
|
||||
} else {
|
||||
cmp = strings.Compare(v, sv.value)
|
||||
if cmp < 0 && sv.left != nil {
|
||||
fmt.Println(" Left: " + sv.left.value)
|
||||
// It's the left node
|
||||
delNode = sv.left
|
||||
} else if cmp > 0 && sv.right != nil {
|
||||
fmt.Println(" Right: " + sv.right.value)
|
||||
// It's the right node
|
||||
delNode = sv.right
|
||||
}
|
||||
if delNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if delNode.left != nil {
|
||||
repNode = delNode.left
|
||||
orphan = delNode.right
|
||||
} else if delNode.right != nil {
|
||||
repNode = delNode.right
|
||||
}
|
||||
if repNode == nil {
|
||||
// No replacement node, we're done
|
||||
return
|
||||
}
|
||||
if cmp < 0 {
|
||||
sv.left = repNode
|
||||
} else if cmp > 0 {
|
||||
sv.right = repNode
|
||||
}
|
||||
}
|
||||
// If we have an orphaned branch, find it a home
|
||||
if orphan != nil {
|
||||
s.findHome(orphan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns if the set contains the given value.
|
||||
func (s *Set) Has(v string) bool {
|
||||
return s.find(v) != nil
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the set is empty or not.
|
||||
func (s *Set) IsEmpty() bool {
|
||||
return s.top.value == ""
|
||||
}
|
||||
|
||||
// Len returns the number of values in the set
|
||||
func (s *Set) Len() int {
|
||||
if !s.IsEmpty() {
|
||||
return s.top.Len()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Slice returns a string slice of the set
|
||||
func (s Set) Slice() []string {
|
||||
if !s.IsEmpty() {
|
||||
return s.top.Slice()
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// String converts the set to a string
|
||||
func (s Set) String() string {
|
||||
ret := "{"
|
||||
if s.top.value != "" {
|
||||
ret += s.top.String()
|
||||
}
|
||||
ret += "}"
|
||||
return ret
|
||||
}
|
||||
|
||||
// find looks for a node with value val, it either returns the node
|
||||
// or an error stating it couldn't find it.
|
||||
func (sv *SetValue) find(val string) (*SetValue, error) {
|
||||
if sv.value == val {
|
||||
return sv, nil
|
||||
}
|
||||
cmp := strings.Compare(val, sv.value)
|
||||
if cmp < 0 && sv.left != nil {
|
||||
return sv.left.find(val)
|
||||
}
|
||||
if cmp > 0 && sv.right != nil {
|
||||
return sv.right.find(val)
|
||||
}
|
||||
return nil, errors.New("Value not found")
|
||||
}
|
||||
|
||||
// findParent looks for the parent of the node with value val
|
||||
// If nil, nil is returned, it _is_ this node.
|
||||
func (sv *SetValue) findParent(val string) (*SetValue, error) {
|
||||
if sv.value == val {
|
||||
// This should only trigger if this is the top node of the tree
|
||||
return nil, nil
|
||||
}
|
||||
cmp := strings.Compare(val, sv.value)
|
||||
if cmp < 0 && sv.left != nil {
|
||||
if sv.left.value == val {
|
||||
return sv, nil
|
||||
}
|
||||
return sv.left.findParent(val)
|
||||
}
|
||||
if cmp > 0 && sv.right != nil {
|
||||
if sv.right.value == val {
|
||||
return sv, nil
|
||||
}
|
||||
return sv.right.findParent(val)
|
||||
}
|
||||
return nil, errors.New("Value not found")
|
||||
}
|
||||
|
||||
func (sv *SetValue) findHome(v *SetValue) {
|
||||
cmp := strings.Compare(v.value, sv.value)
|
||||
if cmp < 0 {
|
||||
if sv.left == nil {
|
||||
sv.left = v
|
||||
} else {
|
||||
sv.left.findHome(v)
|
||||
}
|
||||
} else if cmp > 0 {
|
||||
if sv.right == nil {
|
||||
sv.right = v
|
||||
} else {
|
||||
sv.right.findHome(v)
|
||||
}
|
||||
} else {
|
||||
// Discard the top node, find homes for it's children
|
||||
if v.left != nil {
|
||||
sv.findHome(v.left)
|
||||
}
|
||||
if v.right != nil {
|
||||
sv.findHome(v.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sv *SetValue) Add(v string) {
|
||||
cmp := strings.Compare(v, sv.value)
|
||||
if cmp < 0 {
|
||||
if sv.left == nil {
|
||||
sv.left = &SetValue{value: v}
|
||||
} else {
|
||||
sv.left.Add(v)
|
||||
}
|
||||
} else if cmp > 0 {
|
||||
if sv.right == nil {
|
||||
sv.right = &SetValue{value: v}
|
||||
} else {
|
||||
sv.right.Add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns how many elements are in the branches
|
||||
func (sv *SetValue) Len() int {
|
||||
ret := 1
|
||||
if sv.left != nil {
|
||||
ret += sv.left.Len()
|
||||
}
|
||||
if sv.right != nil {
|
||||
ret += sv.right.Len()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Has checks if this branch contains the value v
|
||||
func (sv *SetValue) Has(v string) bool {
|
||||
ret, _ := sv.find(v)
|
||||
return ret != nil
|
||||
}
|
||||
|
||||
// String gets a string value of this branch
|
||||
func (sv *SetValue) String() string {
|
||||
var ret string
|
||||
if sv.left != nil {
|
||||
ret += sv.left.String() + ", "
|
||||
}
|
||||
ret += "\"" + sv.value + "\""
|
||||
if sv.right != nil {
|
||||
ret += ", " + sv.right.String()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Slice returns a string slice of all values in the branch
|
||||
func (sv *SetValue) Slice() []string {
|
||||
var ret []string
|
||||
if sv.left != nil {
|
||||
ret = sv.left.Slice()
|
||||
}
|
||||
ret = append(ret, sv.value)
|
||||
if sv.right != nil {
|
||||
ret = append(ret, sv.right.Slice()...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Equal returns whether the given sets are the same.
|
||||
func Equal(s1, s2 Set) bool {
|
||||
return s1.String() == s2.String()
|
||||
}
|
||||
|
||||
// Subset returns whether s1 is a subset of s2.
|
||||
func Subset(s1, s2 Set) bool {
|
||||
if s1.Len() == 0 || s2.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
s1Sl := s1.Slice()
|
||||
for i := range s1Sl {
|
||||
if !s2.Has(s1Sl[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Disjoint returns whether two sets _do not_ intersect
|
||||
func Disjoint(s1, s2 Set) bool {
|
||||
s1Sl := s1.Slice()
|
||||
for i := range s1Sl {
|
||||
if s2.Has(s1Sl[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Intersection finds elements that exist in both sets and makes a new
|
||||
// set of them
|
||||
func Intersection(s1, s2 Set) Set {
|
||||
var vals []string
|
||||
s1Sl := s1.Slice()
|
||||
for i := range s1Sl {
|
||||
if s2.Has(s1Sl[i]) {
|
||||
vals = append(vals, s1Sl[i])
|
||||
}
|
||||
}
|
||||
return NewFromSlice(vals)
|
||||
}
|
||||
|
||||
// Union gets all elements in both sets and makes a new set with them.
|
||||
func Union(s1, s2 Set) Set {
|
||||
var vals []string
|
||||
vals = append(vals, s1.Slice()...)
|
||||
vals = append(vals, s2.Slice()...)
|
||||
return NewFromSlice(vals)
|
||||
}
|
||||
|
||||
// Difference returns a Set of all elements in s1 that aren't in s2
|
||||
func Difference(s1, s2 Set) Set {
|
||||
var vals []string
|
||||
s1Sl := s1.Slice()
|
||||
for i := range s1Sl {
|
||||
if !s2.Has(s1Sl[i]) {
|
||||
vals = append(vals, s1Sl[i])
|
||||
}
|
||||
}
|
||||
return NewFromSlice(vals)
|
||||
}
|
||||
|
||||
// SymmetricDifference returns all elements from s1 & s2 that occur in only one of the
|
||||
// sets.
|
||||
func SymmetricDifference(s1, s2 Set) Set {
|
||||
return Union(Difference(s1, s2), Difference(s2, s1))
|
||||
}
|
Reference in New Issue
Block a user