diff --git a/exercises/custom-set/cases_test.go b/exercises/custom-set/cases_test.go index 90523b8ba..97978df43 100644 --- a/exercises/custom-set/cases_test.go +++ b/exercises/custom-set/cases_test.go @@ -1,368 +1,223 @@ package stringset // Source: exercism/x-common -// Commit: 269f498 Merge pull request #48 from soniakeys/custom-set-json +// Commit: cda8f98 Create new exercises structure -// 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. +// Returns true if the set contains no elements var emptyCases = []unaryBoolCase{ - { // empty + { // sets with no elements are empty []string{}, true, }, - { // single element + { // sets with elements are not empty []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. +// Sets can report if they contain an element var hasCases = []eleBoolCase{ - { // nothing is an element of the empty set + { // nothing is contained in an empty set []string{}, "a", false, }, - { // 1 is in the set - []string{"a", "b", "c", "b"}, + { // when the element is in the set + []string{"a", "b", "c"}, "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"}, + { // when the element is not in the set + []string{"a", "b", "c"}, "d", false, }, } -// Test if set1 is a subset of set2. +// A set is a subset if all of its elements are contained in the other set var subsetCases = []binBoolCase{ - { // empty set is subset of itself + { // empty set is a subset of another empty set []string{}, []string{}, true, }, - { // empty set is subset of non-empty set + { // empty set is a subset of non-empty set []string{}, []string{"a"}, true, }, - { // non-empty set is not subset of empty set + { // non-empty set is not a subset of empty set []string{"a"}, []string{}, false, }, - { // non-empty set is subset of itself + { // set is a subset of set with exact same elements []string{"a", "b", "c"}, []string{"a", "b", "c"}, true, }, - { // proper subset + { // set is a subset of larger set with same elements []string{"a", "b", "c"}, []string{"d", "a", "b", "c"}, true, }, - { // same number of elements + { // set is not a subset of set that does not contain its 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. +// Sets are disjoint if they share no elements var disjointCases = []binBoolCase{ { // the empty set is disjoint with itself []string{}, []string{}, true, }, - { // empty set disjoint with non-empty set + { // empty set is disjoint with non-empty set []string{}, []string{"a"}, true, }, - { // non-empty set disjoint with empty set + { // non-empty set is disjoint with empty set []string{"a"}, []string{}, true, }, - { // one element in common + { // sets are not disjoint if they share an element []string{"a", "b"}, []string{"b", "c"}, false, }, - { // no elements in common + { // sets are disjoint if they share no elements []string{"a", "b"}, []string{"c", "d"}, true, }, } -// Produce the union of two sets. -var unionCases = []binOpCase{ - { // union of empty sets - []string{}, - []string{}, +// Sets with the same elements are equal +var eqCases = []binBoolCase{ + { // empty sets are equal []string{}, - }, - { // union of empty set with set of one element []string{}, - []string{"b"}, - []string{"b"}, + true, }, - { // union of empty set with non-empty set + { // empty set is not equal to non-empty set []string{}, - []string{"c", "b", "e"}, - []string{"b", "c", "e"}, + []string{"a", "b", "c"}, + false, }, - { // union of non-empty set with empty set - []string{"a", "c"}, + { // non-empty set is not equal to empty set + []string{"a", "b", "c"}, []string{}, - []string{"a", "c"}, + false, }, - { // union of a set with itself - []string{"a", "c"}, - []string{"c", "a"}, - []string{"a", "c"}, + { // sets with the same elements are equal + []string{"a", "b"}, + []string{"b", "a"}, + true, }, - { // union with one element - []string{"a", "c"}, - []string{"b"}, + { // sets with different elements are not equal []string{"a", "b", "c"}, + []string{"a", "b", "d"}, + false, }, - { // one element in common, one different - []string{"a", "c"}, - []string{"b", "c"}, - []string{"c", "b", "a"}, +} + +// Unique elements can be added to a set +var addCases = []eleOpCase{ + { // add to empty set + []string{}, + "c", + []string{"c"}, }, - { // two elements in common + { // add to non-empty set + []string{"a", "b", "d"}, + "c", []string{"a", "b", "c", "d"}, - []string{"c", "b", "e"}, - []string{"a", "b", "c", "d", "e"}, + }, + { // adding an existing element does not change the set + []string{"a", "b", "c"}, + "c", + []string{"a", "b", "c"}, }, } -// Intersect two sets. +// Intersect returns a set of all shared elements var intersectionCases = []binOpCase{ - { // intersect empty sets + { // intersection of two empty sets is an empty set []string{}, []string{}, []string{}, }, - { // intersect empty set with non-empty set + { // intersection of an empty set and non-empty set is an empty set []string{}, []string{"c", "b", "e"}, []string{}, }, - { // intersect non-empty set with empty set + { // intersection of a non-empty set and an empty set is an 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 + { // intersection of two sets with no shared elements is an empty set []string{"a", "b", "c"}, - []string{"c", "e", "d"}, - []string{"c"}, + []string{"d", "e", "f"}, + []string{}, }, - { // two elements in common, extras in both sets + { // intersection of two sets with shared elements is a set of the shared elements []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) +// Difference (or Complement) of a set is a set of all elements that are only in the first set var differenceCases = []binOpCase{ - { // difference of two empty sets + { // difference of two empty sets is an empty set []string{}, []string{}, []string{}, }, - { // difference of empty set and non-empty set + { // difference of empty set and non-empty set is an empty set []string{}, []string{"c", "b", "e"}, []string{}, }, - { // difference of non-empty set and empty set + { // difference of a non-empty set and an empty set is the non-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 + { // difference of two non-empty sets is a set of elements that are only in the first set []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 +// Union returns a set of all elements in either set +var unionCases = []binOpCase{ + { // union of empty sets is an empty set []string{}, []string{}, []string{}, }, - { // empty set and non-empty set + { // union of an empty set and non-empty set is the non-empty set []string{}, - []string{"c", "b", "e"}, - []string{"c", "b", "e"}, + []string{"b"}, + []string{"b"}, }, - { // non-empty set and empty set - []string{"a", "b", "c", "d"}, + { // union of a non-empty set and empty set is the non-empty set + []string{"a", "c"}, []string{}, - []string{"a", "b", "c", "d"}, - }, - { // no elements in common - []string{"a", "b", "c"}, - []string{"d"}, - []string{"a", "b", "c", "d"}, + []string{"a", "c"}, }, - { // one element in common + { // union of non-empty sets contains all unique elements + []string{"a", "c"}, + []string{"b", "c"}, []string{"c", "b", "a"}, - []string{"b", "d"}, - []string{"a", "c", "d"}, }, } diff --git a/exercises/custom-set/custom_set_test.go b/exercises/custom-set/custom_set_test.go index 7b8d0ba21..b6063ab2b 100644 --- a/exercises/custom-set/custom_set_test.go +++ b/exercises/custom-set/custom_set_test.go @@ -6,20 +6,16 @@ package stringset // // 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 +// (s Set) IsEmpty() bool +// (s Set) Has(string) bool +// Subset(s1, s2 Set) bool // Disjoint(s1, s2 Set) bool +// Equal(s1, s2 Set) bool +// (s Set) Add(string) // Intersection(s1, s2 Set) Set +// Difference(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 @@ -28,12 +24,11 @@ package stringset import ( "math/rand" - "reflect" "strconv" "testing" ) -const targetTestVersion = 3 +const targetTestVersion = 4 func TestTestVersion(t *testing.T) { if testVersion != targetTestVersion { @@ -79,65 +74,36 @@ func TestNewFromSlice(t *testing.T) { } } -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) + // binary function, bool result (Subset, Disjoint, Equal) + binBoolCase struct { + set1 []string + set2 []string + want bool + } + // set-element operator (Add) eleOpCase struct { set []string ele string want []string } - // set-set operator (Union, Intersection, Difference, Symmetric-Difference) + // set-set operator (Intersection, Difference, Union) binOpCase struct { set1 []string set2 []string @@ -145,45 +111,16 @@ type ( } ) -// 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 { +func TestIsEmpty(t *testing.T) { + for _, tc := range emptyCases { 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) + got := s.IsEmpty() + if got != tc.want { + t.Fatalf("%v IsEmpty = %t, want %t", s, got, tc.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) @@ -194,22 +131,14 @@ func TestHas(t *testing.T) { } } -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() +// helper for testing Subset, Disjoint, Equal +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("%v Len = %d, want %d", s, got, tc.want) + t.Fatalf("%s(%v, %v) = %t, want %t", name, s1, s2, got, tc.want) } } } @@ -222,7 +151,22 @@ func TestDisjoint(t *testing.T) { testBinBool("Disjoint", Disjoint, disjointCases, t) } -// helper for testing Union, Intersection, Difference, SymmetricDifference +func TestEqual(t *testing.T) { + testBinBool("Equal", Equal, eqCases, t) +} + +func TestAdd(t *testing.T) { + for _, tc := range addCases { + s := NewFromSlice(tc.set) + s.Add(tc.ele) + want := NewFromSlice(tc.want) + if !Equal(s, want) { + t.Fatalf("%v Add %q = %v, want %v", NewFromSlice(tc.set), tc.ele, s, want) + } + } +} + +// helper for testing Intersection, Difference, Union func testBinOp(name string, f func(Set, Set) Set, cases []binOpCase, t *testing.T) { for _, tc := range cases { s1 := NewFromSlice(tc.set1) @@ -235,10 +179,6 @@ func testBinOp(name string, f func(Set, Set) Set, cases []binOpCase, t *testing. } } -func TestUnion(t *testing.T) { - testBinOp("Union", Union, unionCases, t) -} - func TestIntersection(t *testing.T) { testBinOp("Intersection", Intersection, intersectionCases, t) } @@ -247,8 +187,8 @@ func TestDifference(t *testing.T) { testBinOp("Difference", Difference, differenceCases, t) } -func TestSymmetricDifference(t *testing.T) { - testBinOp("SymmetricDifference", SymmetricDifference, symmetricDifferenceCases, t) +func TestUnion(t *testing.T) { + testBinOp("Union", Union, unionCases, t) } func BenchmarkNewFromSlice1e1(b *testing.B) { bench(1e1, b) } diff --git a/exercises/custom-set/example.go b/exercises/custom-set/example.go index 38e3947e2..1ba4ebebe 100644 --- a/exercises/custom-set/example.go +++ b/exercises/custom-set/example.go @@ -2,12 +2,9 @@ package stringset -import ( - "fmt" - "reflect" -) +import "fmt" -const testVersion = 3 +const testVersion = 4 // Set represents some properties of a mathematical set. // Sets are finite and all elements are unique string values. @@ -27,23 +24,18 @@ func NewFromSlice(l []string) Set { return s } -// Add adds element e to Set s. -// -// If e is already in s, s is unchanged. -func (s Set) Add(e string) { - s[e] = true -} - -// Delete deletes element e from Set s. -// -// If e is not in s, Delete has no effect. -func (s Set) Delete(e string) { - delete(s, e) -} - -// Has returns true if e is an element of s. -func (s Set) Has(e string) bool { - return s[e] +// String returns a printable representation of s. +func (s Set) String() string { + r := "{" + i := 0 + for e := range s { + if i > 0 { + r += ", " + } + r += fmt.Sprintf("%q", e) + i++ + } + return r + "}" } // IsEmpty returns true if s represents the empty set. @@ -51,44 +43,40 @@ func (s Set) IsEmpty() bool { return len(s) == 0 } -// Len returns the cardinality, or number of elements in s. -func (s Set) Len() int { - return len(s) +// Has returns true if e is an element of s. +func (s Set) Has(e string) bool { + return s[e] } -// Slice constructs a slice populated with the elements of s. -func (s Set) Slice() []string { - l := make([]string, len(s)) - i := 0 - for e := range s { - l[i] = e - i++ - } - return l +// Add adds element e to Set s. +// If e is already in s, s is unchanged. +func (s Set) Add(e string) { + s[e] = true } -// String returns a printable representation of s. -func (s Set) String() string { - r := "{" - i := 0 - for e := range s { - if i > 0 { - r += ", " +// Subset returns true if all elements of s1 are also in s2. +func Subset(s1, s2 Set) bool { + for e := range s1 { + if !s2[e] { + return false } - r += fmt.Sprintf("%q", e) - i++ } - return r + "}" + return true } // Disjoint returns true if s1 and s2 have no elements in common. func Disjoint(s1, s2 Set) bool { - return len(Intersection(s1, s2)) == 0 + for e := range s1 { + if s2[e] { + return false + } + } + return true } // Equal returns true if s1 and s2 contain the same elements. func Equal(s1, s2 Set) bool { - return reflect.DeepEqual(s1, s2) + return Subset(s1, s2) && Subset(s2, s1) } // Intersection constructs a new Set populated with the elements common to @@ -103,19 +91,6 @@ func Intersection(s1, s2 Set) Set { return r } -// Union constructs a new Set populated with elements that appear in s1, s2, -// or both. -func Union(s1, s2 Set) Set { - r := Set{} - for e := range s1 { - r[e] = true - } - for e := range s2 { - r[e] = true - } - return r -} - // Difference returns a new Set populated with elements that appear in s1 // but not in s2. func Difference(s1, s2 Set) Set { @@ -128,29 +103,15 @@ func Difference(s1, s2 Set) Set { return r } -// Subset returns true if all elements of s1 are also in s2. -func Subset(s1, s2 Set) bool { - for e := range s1 { - if !s2[e] { - return false - } - } - return true -} - -// SymmetricDifference constructs a new set populated with elements that are -// members of s1 or s2 but not both. -func SymmetricDifference(s1, s2 Set) Set { +// Union constructs a new Set populated with elements that appear in s1, s2, +// or both. +func Union(s1, s2 Set) Set { r := Set{} for e := range s1 { - if !s2[e] { - r[e] = true - } + r[e] = true } for e := range s2 { - if !s1[e] { - r[e] = true - } + r[e] = true } return r } diff --git a/exercises/custom-set/example_gen.go b/exercises/custom-set/example_gen.go index 0f0acc182..7202ddf43 100644 --- a/exercises/custom-set/example_gen.go +++ b/exercises/custom-set/example_gen.go @@ -42,70 +42,58 @@ func strSlice(ns []int) []string { // The JSON structure we expect to be able to unmarshal into type js struct { - Equal binaryBool // binary function, boolean result - Add eleOp // set-element operator - Delete eleOp // ... - IsEmpty unaryBool `json:"is-empty"` - Size unaryInt // Set.Len - Element eleBool // Set.Has - Subset binaryBool - Disjoint binaryBool - Union binaryOp // set-set operator - Intersection binaryOp - Difference binaryOp - SymmetricDifference binaryOp `json:"symmetric-difference"` + IsEmpty unaryBool `json:"empty"` + Contains eleBool + Subset binaryBool + Disjoint binaryBool + Equal binaryBool + Add eleOp + Intersection binaryOp + Difference binaryOp + Union binaryOp } -type binaryBool struct { - Description []string +type unaryBool struct { + Description string Cases []struct { Description string - Set1 []int - Set2 []int + Set []int Expected bool } } -type eleOp struct { - Description []string +type eleBool struct { + Description string Cases []struct { Description string Set []int Element int - Expected []int - } -} - -type unaryBool struct { - Description []string - Cases []struct { - Description string - Set []int Expected bool } } -type unaryInt struct { - Description []string +type binaryBool struct { + Description string Cases []struct { Description string - Set []int - Expected int + Set1 []int + Set2 []int + Expected bool } } -type eleBool struct { - Description []string +type eleOp struct { + Description string Cases []struct { Description string Set []int Element int - Expected bool + Expected []int } } type binaryOp struct { - Description []string + Description string Cases []struct { Description string Set1 []int @@ -122,9 +110,6 @@ type binaryOp struct { var tmpl = ` {{/* nested templates for repeated stuff */}} -{{define "cmts"}}{{range .}}// {{.}} -{{end}}{{end}} - {{define "binaryBool"}} = []binBoolCase{ {{range .}}{ // {{.Description}} {{strs .Set1 | printf "%#v"}}, @@ -156,46 +141,41 @@ package stringset {{if .Commit}}// Commit: {{.Commit}} {{end}} -{{template "cmts" .J.Equal.Description}}var eqCases{{template "binaryBool" .J.Equal.Cases}} - -{{template "cmts" .J.Add.Description}}var addCases{{template "eleOp" .J.Add.Cases}} - -{{template "cmts" .J.Delete.Description}}var delCases{{template "eleOp" .J.Delete.Cases}} - -{{range .J.IsEmpty.Description}}// {{.}} -{{end}}var emptyCases = []unaryBoolCase{ +// {{.J.IsEmpty.Description}} +var emptyCases = []unaryBoolCase{ {{range .J.IsEmpty.Cases}}{ // {{.Description}} {{strs .Set | printf "%#v"}}, {{.Expected}}, }, {{end}}} -{{range .J.Size.Description}}// {{.}} -{{end}}var lenCases = []unaryIntCase{ -{{range .J.Size.Cases}}{ // {{.Description}} - {{strs .Set | printf "%#v"}}, - {{.Expected}}, -}, -{{end}}} - -{{range .J.Element.Description}}// {{.}} -{{end}}var hasCases = []eleBoolCase{ -{{range .J.Element.Cases}}{ // {{.Description}} +// {{.J.Contains.Description}} +var hasCases = []eleBoolCase{ +{{range .J.Contains.Cases}}{ // {{.Description}} {{strs .Set | printf "%#v"}}, {{str .Element | printf "%q"}}, {{.Expected}}, }, {{end}}} -{{template "cmts" .J.Subset.Description}}var subsetCases{{template "binaryBool" .J.Subset.Cases}} +// {{.J.Subset.Description}} +var subsetCases{{template "binaryBool" .J.Subset.Cases}} + +// {{.J.Disjoint.Description}} +var disjointCases{{template "binaryBool" .J.Disjoint.Cases}} -{{template "cmts" .J.Disjoint.Description}}var disjointCases{{template "binaryBool" .J.Disjoint.Cases}} +// {{.J.Equal.Description}} +var eqCases{{template "binaryBool" .J.Equal.Cases}} -{{template "cmts" .J.Union.Description}}var unionCases{{template "binaryOp" .J.Union.Cases}} +// {{.J.Add.Description}} +var addCases{{template "eleOp" .J.Add.Cases}} -{{template "cmts" .J.Intersection.Description}}var intersectionCases{{template "binaryOp" .J.Intersection.Cases}} +// {{.J.Intersection.Description}} +var intersectionCases{{template "binaryOp" .J.Intersection.Cases}} -{{template "cmts" .J.Difference.Description}}var differenceCases{{template "binaryOp" .J.Difference.Cases}} +// {{.J.Difference.Description}} +var differenceCases{{template "binaryOp" .J.Difference.Cases}} -{{template "cmts" .J.SymmetricDifference.Description}}var symmetricDifferenceCases{{template "binaryOp" .J.SymmetricDifference.Cases}} +// {{.J.Union.Description}} +var unionCases{{template "binaryOp" .J.Union.Cases}} ` diff --git a/exercises/custom-set/example_slice.go b/exercises/custom-set/example_slice.go index 4131e2bb4..9882c1ac5 100644 --- a/exercises/custom-set/example_slice.go +++ b/exercises/custom-set/example_slice.go @@ -5,19 +5,25 @@ package stringset // Example based on slices rather than maps, to validate that such a solution // can pass the tests. // -// The API is a little akward for this because it has value receivers but +// The API is a little awkward for this because it has value receivers but // a pointer in a struct works okay. import "fmt" -const testVersion = 3 +const testVersion = 4 -type Set struct{ s *[]string } +// Set represents some properties of a mathematical set. +// Sets are finite and all elements are unique string values. +type Set struct { + s *[]string +} +// New constructs a new Set representing an empty set. func New() Set { return Set{new([]string)} } +// NewFromSlice constructs a new Set populated from elements of a slice. func NewFromSlice(l []string) Set { s := New() for _, e := range l { @@ -26,27 +32,26 @@ func NewFromSlice(l []string) Set { return s } -func (s Set) Add(v string) { - for _, e := range *s.s { - if e == v { - return +// String returns a printable representation of s. +func (s Set) String() string { + r := "{" + i := 0 + for _, v := range *s.s { + if i > 0 { + r += ", " } + r += fmt.Sprintf("%q", v) + i++ } - *s.s = append(*s.s, v) + return r + "}" } -func (s Set) Delete(v string) { - ss := *s.s - for i, e := range ss { - if e == v { - last := len(ss) - 1 - ss[i] = ss[last] - *s.s = ss[:last] - return - } - } +// IsEmpty returns true if s represents the empty set. +func (s Set) IsEmpty() bool { + return len(*s.s) == 0 } +// Has returns true if e is an element of s. func (s Set) Has(v string) bool { for _, e := range *s.s { if e == v { @@ -56,31 +61,28 @@ func (s Set) Has(v string) bool { return false } -func (s Set) IsEmpty() bool { - return len(*s.s) == 0 -} - -func (s Set) Len() int { - return len(*s.s) -} - -func (s Set) Slice() []string { - return append([]string{}, *s.s...) +// Add adds element e to Set s. +// If e is already in s, s is unchanged. +func (s Set) Add(v string) { + for _, e := range *s.s { + if e == v { + return + } + } + *s.s = append(*s.s, v) } -func (s Set) String() string { - r := "{" - i := 0 - for _, v := range *s.s { - if i > 0 { - r += ", " +// Subset returns true if all elements of s1 are also in s2. +func Subset(s1, s2 Set) bool { + for _, v := range *s1.s { + if !s2.Has(v) { + return false } - r += fmt.Sprintf("%q", v) - i++ } - return r + "}" + return true } +// Disjoint returns true if s1 and s2 have no elements in common. func Disjoint(s1, s2 Set) bool { for _, v := range *s1.s { if s2.Has(v) { @@ -90,10 +92,13 @@ func Disjoint(s1, s2 Set) bool { return true } +// Equal returns true if s1 and s2 contain the same elements. func Equal(s1, s2 Set) bool { - return s1.Len() == s2.Len() && Subset(s1, s2) + return Subset(s1, s2) && Subset(s2, s1) } +// Intersection constructs a new Set populated with the elements common to +// both s1 and s2 func Intersection(s1, s2 Set) Set { var i []string for _, v := range *s1.s { @@ -104,15 +109,8 @@ func Intersection(s1, s2 Set) Set { return Set{&i} } -func Union(s1, s2 Set) Set { - s := s1.Slice() - u := Set{&s} - for _, v := range *s2.s { - u.Add(v) - } - return u -} - +// Difference returns a new Set populated with elements that appear in s1 +// but not in s2. func Difference(s1, s2 Set) Set { var d []string for _, v := range *s1.s { @@ -123,29 +121,13 @@ func Difference(s1, s2 Set) Set { return Set{&d} } -// Subset returns true if all elements of s1 are also in s2. -func Subset(s1, s2 Set) bool { - for _, v := range *s1.s { - if !s2.Has(v) { - return false - } - } - return true -} - -// SymmetricDifference constructs a new set populated with elements that are -// members of s1 or s2 but not both. -func SymmetricDifference(s1, s2 Set) Set { - var s []string - for _, v := range *s1.s { - if !s2.Has(v) { - s = append(s, v) - } - } +// Union constructs a new Set populated with elements that appear in s1, s2, +// or both. +func Union(s1, s2 Set) Set { + s := append([]string{}, *s1.s...) + u := Set{&s} for _, v := range *s2.s { - if !s1.Has(v) { - s = append(s, v) - } + u.Add(v) } - return Set{&s} + return u }