From e3d678b04f6c4a24f49dd55ac46224b5f8607208 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 30 Aug 2016 02:32:55 +0200 Subject: Solve strain --- go/strain/README.md | 53 +++++++++++++++ go/strain/strain.go | 59 ++++++++++++++++ go/strain/strain_test.go | 170 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 go/strain/README.md create mode 100644 go/strain/strain.go create mode 100644 go/strain/strain_test.go 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) + } + } +} -- cgit v1.2.3