summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2016-08-30 02:32:55 +0200
committerDimitri Sokolyuk <demon@dim13.org>2016-08-30 02:32:55 +0200
commite3d678b04f6c4a24f49dd55ac46224b5f8607208 (patch)
treeffc8ffa8800bd76776de6af694a049d7580d735b
parent4fbacfaf2a8fdac29e06df4b09b92ae228a55b51 (diff)
Solve strain
-rw-r--r--go/strain/README.md53
-rw-r--r--go/strain/strain.go59
-rw-r--r--go/strain/strain_test.go170
3 files changed, 282 insertions, 0 deletions
diff --git a/go/strain/README.md b/go/strain/README.md
new file mode 100644
index 0000000..26c9d2f
--- /dev/null
+++ b/go/strain/README.md
@@ -0,0 +1,53 @@
+# Strain
+
+Implement the `keep` and `discard` operation on collections. Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false.
+
+Write two functions that each take a function and a list. One of them will
+return the list of items for which the passed in function is true, and the
+other will return the items for which it is false.
+
+For example, given the collection of numbers:
+
+- 1, 2, 3, 4, 5
+
+And the predicate:
+
+- is the number even?
+
+Then your keep operation should produce:
+
+- 2, 4
+
+While your discard operation should produce:
+
+- 1, 3, 5
+
+Note that the union of keep and discard is all the elements.
+
+The functions may be called `keep` and `discard`, or they may need different
+names in order to not clash with existing functions or concepts in your
+language.
+
+## Restrictions
+
+Keep your hands off that filter/reject/whatchamacallit functionality
+provided by your standard library! Solve this one yourself using other
+basic tools instead.
+
+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).
+
+## Source
+
+Conversation with James Edward Gray II [https://twitter.com/jeg2](https://twitter.com/jeg2)
+
+## Submitting Incomplete Problems
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.
+
diff --git a/go/strain/strain.go b/go/strain/strain.go
new file mode 100644
index 0000000..487b190
--- /dev/null
+++ b/go/strain/strain.go
@@ -0,0 +1,59 @@
+package strain
+
+type Ints []int
+
+func (i Ints) Keep(f func(int) bool) Ints {
+ r := Ints{}
+ for _, v := range i {
+ if f(v) {
+ r = append(r, v)
+ }
+ }
+ if len(r) == 0 {
+ return nil
+ }
+ return r
+}
+
+func (i Ints) Discard(f func(int) bool) Ints {
+ r := Ints{}
+ for _, v := range i {
+ if !f(v) {
+ r = append(r, v)
+ }
+ }
+ if len(r) == 0 {
+ return nil
+ }
+ return r
+}
+
+type Lists [][]int
+
+func (l Lists) Keep(f func([]int) bool) Lists {
+ r := Lists{}
+ for _, v := range l {
+ if f(v) {
+ r = append(r, v)
+ }
+ }
+ if len(r) == 0 {
+ return nil
+ }
+ return r
+}
+
+type Strings []string
+
+func (s Strings) Keep(f func(string) bool) Strings {
+ r := Strings{}
+ for _, v := range s {
+ if f(v) {
+ r = append(r, v)
+ }
+ }
+ if len(r) == 0 {
+ return nil
+ }
+ return r
+}
diff --git a/go/strain/strain_test.go b/go/strain/strain_test.go
new file mode 100644
index 0000000..dd3da5a
--- /dev/null
+++ b/go/strain/strain_test.go
@@ -0,0 +1,170 @@
+// Collections, hm? For this exercise in Go you'll work with slices as
+// collections. Define the following in your solution:
+//
+// type Ints []int
+// type Lists [][]int
+// type Strings []string
+//
+// Then complete the exercise by implementing these methods:
+//
+// (Ints) Keep(func(int) bool) Ints
+// (Ints) Discard(func(int) bool) Ints
+// (Lists) Keep(func([]int) bool) Lists
+// (Strings) Keep(func(string) bool) Strings
+
+package strain
+
+import (
+ "reflect"
+ "testing"
+)
+
+func lt10(x int) bool { return x < 10 }
+func gt10(x int) bool { return x > 10 }
+func odd(x int) bool { return x&1 == 1 }
+func even(x int) bool { return x&1 == 0 }
+
+var keepTests = []struct {
+ pred func(int) bool
+ list Ints
+ want Ints
+}{
+ {lt10,
+ nil,
+ nil},
+ {lt10,
+ Ints{1, 2, 3},
+ Ints{1, 2, 3}},
+ {odd,
+ Ints{1, 2, 3},
+ Ints{1, 3}},
+ {even,
+ Ints{1, 2, 3, 4, 5},
+ Ints{2, 4}},
+}
+
+func TestKeepInts(t *testing.T) {
+ for _, test := range keepTests {
+ // setup here copies test.list, preserving the nil value if it is nil
+ // and making a fresh copy of the underlying array otherwise.
+ cp := test.list
+ if cp != nil {
+ cp = append(Ints{}, cp...)
+ }
+ switch res := cp.Keep(test.pred); {
+ case !reflect.DeepEqual(cp, test.list):
+ t.Fatalf("Ints%v.Keep() should not modify its reciever. "+
+ "Found %v, reciever should stay %v",
+ test.list, cp, test.list)
+ case !reflect.DeepEqual(res, test.want):
+ t.Fatalf("Ints%v.Keep() = %v, want %v",
+ test.list, res, test.want)
+ }
+ }
+}
+
+var discardTests = []struct {
+ pred func(int) bool
+ list Ints
+ want Ints
+}{
+ {lt10,
+ nil,
+ nil},
+ {gt10,
+ Ints{1, 2, 3},
+ Ints{1, 2, 3}},
+ {odd,
+ Ints{1, 2, 3},
+ Ints{2}},
+ {even,
+ Ints{1, 2, 3, 4, 5},
+ Ints{1, 3, 5}},
+}
+
+func TestDiscardInts(t *testing.T) {
+ for _, test := range discardTests {
+ cp := test.list
+ if cp != nil {
+ cp = append(Ints{}, cp...) // dup underlying array
+ }
+ switch res := cp.Discard(test.pred); {
+ case !reflect.DeepEqual(cp, test.list):
+ t.Fatalf("Ints%v.Discard() should not modify its reciever. "+
+ "Found %v, reciever should stay %v",
+ test.list, cp, test.list)
+ case !reflect.DeepEqual(res, test.want):
+ t.Fatalf("Ints%v.Discard() = %v, want %v",
+ test.list, res, test.want)
+ }
+ }
+}
+
+func TestKeepStrings(t *testing.T) {
+ zword := func(s string) bool { return len(s) > 0 && s[0] == 'z' }
+ list := Strings{"apple", "zebra", "banana", "zombies", "cherimoya", "zelot"}
+ want := Strings{"zebra", "zombies", "zelot"}
+
+ cp := append(Strings{}, list...) // make copy, as with TestInts
+ switch res := cp.Keep(zword); {
+ case !reflect.DeepEqual(cp, list):
+ t.Fatalf("Strings%v.Keep() should not modify its reciever. "+
+ "Found %v, reciever should stay %v",
+ list, cp, list)
+ case !reflect.DeepEqual(res, want):
+ t.Fatalf("Strings%v.Keep() = %v, want %v",
+ list, res, want)
+ }
+}
+
+func TestKeepLists(t *testing.T) {
+ has5 := func(l []int) bool {
+ for _, e := range l {
+ if e == 5 {
+ return true
+ }
+ }
+ return false
+ }
+ list := Lists{
+ {1, 2, 3},
+ {5, 5, 5},
+ {5, 1, 2},
+ {2, 1, 2},
+ {1, 5, 2},
+ {2, 2, 1},
+ {1, 2, 5},
+ }
+ want := Lists{
+ {5, 5, 5},
+ {5, 1, 2},
+ {1, 5, 2},
+ {1, 2, 5},
+ }
+ cp := append(Lists{}, list...)
+ switch res := cp.Keep(has5); {
+ case !reflect.DeepEqual(cp, list):
+ t.Fatalf("Lists%v.Keep() should not modify its reciever. "+
+ "Found %v, reciever should stay %v",
+ list, cp, list)
+ case !reflect.DeepEqual(res, want):
+ t.Fatalf("Lists%v.Keep() = %v, want %v",
+ list, res, want)
+ }
+}
+
+func BenchmarkKeepInts(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, test := range keepTests {
+ test.list.Keep(test.pred)
+ }
+ }
+}
+
+func BenchmarkDiscardInts(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, test := range discardTests {
+ test.list.Discard(test.pred)
+ }
+ }
+}