Merge Go Work

This commit is contained in:
Brian Buller 2016-08-23 15:47:19 -05:00
parent b8814259b5
commit 9e58d17c5c
25 changed files with 941 additions and 50 deletions

View File

@ -0,0 +1,46 @@
# Atbash Cipher
Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.
The Atbash cipher is a simple substitution cipher that relies on
transposing all the letters in the alphabet such that the resulting
alphabet is backwards. The first letter is replaced with the last
letter, the second with the second-last, and so on.
An Atbash cipher for the Latin alphabet would be as follows:
```plain
Plain: abcdefghijklmnopqrstuvwxyz
Cipher: zyxwvutsrqponmlkjihgfedcba
```
It is a very weak cipher because it only has one possible key, and it is
a simple monoalphabetic substitution cipher. However, this may not have
been an issue in the cipher's time.
Ciphertext is written out in groups of fixed length, the traditional group size
being 5 letters, and punctuation is excluded. This is to make it harder to guess
things based on word boundaries.
## Examples
- Encoding `test` gives `gvhg`
- Decoding `gvhg` gives `test`
- Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `The quick brown fox jumps over the lazy dog.`
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
Wikipedia [http://en.wikipedia.org/wiki/Atbash](http://en.wikipedia.org/wiki/Atbash)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,36 @@
package atbash
import "strings"
func Atbash(s string) string {
s = strings.ToLower(s)
var v []byte
var b, iter int
for i := range s {
sv := int(s[i])
switch {
case sv < 48:
continue
case sv >= 48 && sv <= 57:
b = sv
case sv < 109:
b = 110 + (109 - sv)
case sv == 109:
b = 110
case sv == 110:
b = 109
case sv > 110:
b = 109 - (sv - 110)
}
v = append(v, byte(b))
iter++
if iter == 5 {
v = append(v, ' ')
iter = 0
}
}
if iter == 0 {
return string(v)[:len(string(v))-1]
}
return string(v)
}

View File

@ -0,0 +1,40 @@
package atbash
import "testing"
var tests = []struct {
expected string
s string
}{
{"ml", "no"},
{"ml", "no"},
{"bvh", "yes"},
{"lnt", "OMG"},
{"lnt", "O M G"},
{"nrmwy oldrm tob", "mindblowingly"},
{"gvhgr mt123 gvhgr mt", "Testing, 1 2 3, testing."},
{"gifgs rhurx grlm", "Truth is fiction."},
{"gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt", "The quick brown fox jumps over the lazy dog."},
}
func TestAtbash(t *testing.T) {
for _, test := range tests {
actual := Atbash(test.s)
if actual != test.expected {
t.Errorf("Atbash(%s): expected %s, actual %s", test.s, test.expected, actual)
}
}
}
func BenchmarkAtbash(b *testing.B) {
b.StopTimer()
for _, test := range tests {
b.StartTimer()
for i := 0; i < b.N; i++ {
Atbash(test.s)
}
b.StopTimer()
}
}

BIN
go/atbash-cipher/cmd/cmd Executable file

Binary file not shown.

View File

@ -0,0 +1,11 @@
package main
import (
"fmt"
"../../atbash-cipher"
)
func main() {
fmt.Println(atbash.Atbash("mindblowingly"))
}

View File

@ -1 +1 @@
custom-set
prime-factors

Binary file not shown.

View File

@ -11,9 +11,9 @@ func main() {
s1 := stringset.NewFromSlice([]string{"a"})
addAndOutput(s1, "b")
addAndOutput(s1, "d")
addAndOutput(s1, "c")
s1.PrettyPrint()
return
addAndOutput(s1, "c")
addAndOutput(s1, "0")
addAndOutput(s1, "aa")
addAndOutput(s1, "aaa")

View File

@ -27,7 +27,6 @@ package stringset
// Format the empty set as {}.
import (
"fmt"
"math/rand"
"reflect"
"strconv"
@ -167,17 +166,13 @@ func TestEqual(t *testing.T) {
// helper for testing Add, Delete
func testEleOp(name string, op func(Set, string), cases []eleOpCase, t *testing.T) {
for _, tc := range cases {
fmt.Print("Running Test Case: ")
fmt.Println(tc)
s := NewFromSlice(tc.set)
op(s, tc.ele)
want := NewFromSlice(tc.want)
if !Equal(s, want) {
fmt.Println(s.String())
t.Fatalf("%v %s %q = %v, want %v",
NewFromSlice(tc.set), name, tc.ele, s, want)
}
fmt.Println("=== Done ===")
}
}

View File

@ -3,7 +3,6 @@ package stringset
import (
"errors"
"fmt"
"strconv"
"strings"
)
@ -45,20 +44,12 @@ func (s Set) Add(v string) {
return
}
if !s.Has(v) {
bef := "Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue()
newNode := s.top.Add(&Node{value: v})
fmt.Println(bef)
fmt.Println("Current New Node: " + newNode.value + ";L:" + newNode.getLeftValue() + ";R:" + newNode.getRightValue())
fmt.Printf("newNode.left: %v (%v)\n", &newNode.left, newNode.left)
s.top.value = newNode.value
s.top.left = newNode.left
s.top.right = newNode.right
fmt.Println("Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue())
fmt.Printf("s.top.left: %v (%v)\n", &s.top.left, s.top.left)
fmt.Println("Add Done")
//s.top = s.top.Add(newNode)
//fmt.Println("Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue())
}
}
@ -274,63 +265,43 @@ func (sv *Node) findHome(v *Node) {
}
func (sv *Node) Add(n *Node) *Node {
fmt.Println("1 Adding at Node " + sv.value)
cmp := strings.Compare(n.value, sv.value)
if cmp < 0 {
fmt.Println("2 checking to left")
if sv.left == nil {
fmt.Println("3 left is nil, add it")
sv.left = n
} else {
fmt.Println("--> 4 recurse left")
sv.left = sv.left.Add(n)
fmt.Println("<-- 5 added to left")
}
} else {
fmt.Println("6 checking to right")
if sv.right == nil {
fmt.Println("7 right is nil, add it")
sv.right = n
} else {
fmt.Println("--> 8 recurse right")
sv.right = sv.right.Add(n)
fmt.Println("<-- 9 added to right")
}
}
fmt.Println("10 let's balance now (sv: " + sv.value + ")")
newVal := sv
/* TODO: Fix the tree balancing
balance := GetBalance(sv)
// left left unbalance
fmt.Println("11 balance: " + strconv.Itoa(balance))
newVal := sv
if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) < 0 { // < sv.left.value {
fmt.Println("12 Left Left")
if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) < 0 {
newVal = RightRotate(sv)
fmt.Println("13")
}
// right right unbalance
if balance < -1 && strings.Compare(n.value, sv.getRightValue()) > 0 { //n.value > sv.right.value {
fmt.Println("14 Right Right")
if balance < -1 && strings.Compare(n.value, sv.getRightValue()) > 0 {
newVal = LeftRotate(sv)
fmt.Println("15")
}
// left right unbalance
if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) > 0 { //n.value > sv.left.value {
fmt.Println("16 Left Right")
if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) > 0 {
sv.left = LeftRotate(sv.left)
fmt.Println("17")
newVal = RightRotate(sv)
fmt.Println("18")
}
// right left unbalance
if balance < -1 && strings.Compare(n.value, sv.getRightValue()) < 0 { //n.value < sv.right.value {
fmt.Println("19 Right Left")
if balance < -1 && strings.Compare(n.value, sv.getRightValue()) < 0 {
sv.right = RightRotate(sv.right)
fmt.Println("20")
newVal = LeftRotate(sv)
fmt.Println("21")
}
fmt.Println("22 end add")
*/
return newVal
}
@ -476,9 +447,6 @@ func RightRotate(y *Node) *Node {
// Perform rotation
x.right = y
y.left = t2
fmt.Println("RightRotate: return=" + x.value + "; L:" + x.left.value + "; R:" + x.right.value)
fmt.Println("RightRotate: leftNode=" + x.getLeftValue() + "; L:" + x.left.getLeftValue() + "; R:" + x.left.getRightValue())
fmt.Println("RightRotate: rightNode=" + x.getRightValue() + "; L:" + x.right.getLeftValue() + "; R:" + x.right.getRightValue())
// Return new root
return x
}
@ -490,9 +458,6 @@ func LeftRotate(x *Node) *Node {
y.left = x
x.right = t2
// Return new root
fmt.Println("LeftRotate: return=" + y.value + "; L:" + y.left.value + "; R:" + y.right.value)
fmt.Println("LeftRotate: leftNode=" + y.getLeftValue() + "; L:" + y.left.getLeftValue() + "; R:" + y.left.getRightValue())
fmt.Println("LeftRotate: rightNode=" + y.getRightValue() + "; L:" + y.right.getLeftValue() + "; R:" + y.right.getRightValue())
return y
}

58
go/hello-world/README.md Normal file
View File

@ -0,0 +1,58 @@
# Hello World
Write a function that greets the user by name, or by saying "Hello, World!" if no name is given.
["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is the traditional first program for beginning programming in a new language.
**Note:** You can skip this exercise by running:
exercism skip $LANGUAGE hello-world
## Specification
Write a `Hello World!` function that can greet someone given their name.
The function should return the appropriate greeting.
For an input of "Alice", the response should be "Hello, Alice!".
If a name is not given, the response should be "Hello, World!"
## Test-Driven Development
As programmers mature, they eventually want to test their code.
Here at Exercism we simulate [Test-Driven Development](http://en.wikipedia.org/wiki/Test-driven_development) (TDD), where you write your tests before writing any functionality. The simulation comes in the form of a pre-written test suite, which will signal that you have solved the problem.
It will also provide you with a safety net to explore other solutions without breaking the functionality.
### A typical TDD workflow on Exercism:
1. Run the test file and pick one test that's failing.
2. Write some code to fix the test you picked.
3. Re-run the tests to confirm the test is now passing.
4. Repeat from step 1.
5. Submit your solution (`exercism submit /path/to/file`)
## Instructions
Submissions are encouraged to be general, within reason. Having said that, it's also important not to over-engineer a solution.
It's important to remember that the goal is to make code as expressive and readable as we can. However, solutions to the hello-world exercise will not be reviewed by a person, but by rikki- the robot, who will offer an encouraging word.
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
This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,29 @@
package hello
import "testing"
// Define a function HelloWorld(string) string.
//
// Also define a testVersion with a value that matches
// the targetTestVersion here.
const targetTestVersion = 2
func TestHelloWorld(t *testing.T) {
tests := []struct {
name, expected string
}{
{"", "Hello, World!"},
{"Gopher", "Hello, Gopher!"},
}
for _, test := range tests {
observed := HelloWorld(test.name)
if observed != test.expected {
t.Fatalf("HelloWorld(%s) = %v, want %v", test.name, observed, test.expected)
}
}
if testVersion != targetTestVersion {
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
}
}

View File

@ -0,0 +1,11 @@
package hello
const testVersion = 2
// HelloWorld takes a string (optionally empty) and says hello to it!
func HelloWorld(s string) string {
if s == "" {
s = "World"
}
return "Hello, " + s + "!"
}

38
go/phone-number/README.md Normal file
View File

@ -0,0 +1,38 @@
# Phone Number
Write a program that cleans up user-entered phone numbers so that they can be sent SMS messages.
The rules are as follows:
- If the phone number is less than 10 digits assume that it is bad
number
- If the phone number is 10 digits assume that it is good
- If the phone number is 11 digits and the first number is 1, trim the 1
and use the last 10 digits
- If the phone number is 11 digits and the first number is not 1, then
it is a bad number
- If the phone number is more than 11 digits assume that it is a bad
number
We've provided tests, now make them pass.
Hint: Only make one test pass at a time. Disable the others, then flip
each on in turn after you get the current failing one to pass.
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
Event Manager by JumpstartLab [http://tutorials.jumpstartlab.com/projects/eventmanager.html](http://tutorials.jumpstartlab.com/projects/eventmanager.html)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,118 @@
package phonenumber
import (
"testing"
)
type testCase struct {
input string
expected string
expectErr bool
}
var numberTests = []testCase{
{"(123) 456-7890", "1234567890", false},
{"123.456.7890", "1234567890", false},
{"1234567890", "1234567890", false},
{"12345678901234567", "", true},
{"21234567890", "", true},
{"123456789", "", true},
}
func TestNumber(t *testing.T) {
for _, test := range numberTests {
actual, actualErr := Number(test.input)
if actual != test.expected {
t.Errorf("Number(%s): expected [%s], actual: [%s]", test.input, test.expected, actual)
}
// if we expect an error and there isn't one
if test.expectErr && actualErr == nil {
t.Errorf("Number(%s): expected an error, but error is nil", test.input)
}
// if we don't expect an error and there is one
if !test.expectErr && actualErr != nil {
t.Errorf("Number(%s): expected no error, but error is: %s", test.input, actualErr)
}
}
}
func BenchmarkNumber(b *testing.B) {
b.StopTimer()
for _, test := range numberTests {
b.StartTimer()
for i := 0; i < b.N; i++ {
Number(test.input)
}
b.StopTimer()
}
}
var areaCodeTests = []testCase{
{"1234567890", "123", false},
{"213.456.7890", "213", false},
{"213.456.7890.2345", "", true},
{"213.456", "", true},
}
func TestAreaCode(t *testing.T) {
for _, test := range areaCodeTests {
actual, actualErr := AreaCode(test.input)
if actual != test.expected {
t.Errorf("AreaCode(%s): expected [%s], actual: [%s]", test.input, test.expected, actual)
}
// if we expect an error and there isn't one
if test.expectErr && actualErr == nil {
t.Errorf("AreaCode(%s): expected an error, but error is nil", test.input)
}
// if we don't expect an error and there is one
if !test.expectErr && actualErr != nil {
t.Errorf("AreaCode(%s): expected no error, but error is: %s", test.input, actualErr)
}
}
}
func BenchmarkAreaCode(b *testing.B) {
b.StopTimer()
for _, test := range areaCodeTests {
b.StartTimer()
for i := 0; i < b.N; i++ {
AreaCode(test.input)
}
b.StopTimer()
}
}
var formatTests = []testCase{
{"1234567890", "(123) 456-7890", false},
{"11234567890", "(123) 456-7890", false},
{"112345", "", true},
{"11234590870986", "", true},
}
func TestFormat(t *testing.T) {
for _, test := range formatTests {
actual, actualErr := Format(test.input)
if actual != test.expected {
t.Errorf("Format(%s): expected [%s], actual: [%s]", test.input, test.expected, actual)
}
// if we expect an error and there isn't one
if test.expectErr && actualErr == nil {
t.Errorf("Format(%s): expected an error, but error is nil", test.input)
}
// if we don't expect an error and there is one
if !test.expectErr && actualErr != nil {
t.Errorf("Format(%s): expected no error, but error is: %s", test.input, actualErr)
}
}
}
func BenchmarkFormat(b *testing.B) {
b.StopTimer()
for _, test := range areaCodeTests {
b.StartTimer()
for i := 0; i < b.N; i++ {
Format(test.input)
}
b.StopTimer()
}
}

View File

@ -0,0 +1,44 @@
package phonenumber
import (
"errors"
"strings"
)
// Number takes a number and validates it
func Number(num string) (string, error) {
num = strings.Map(func(r rune) rune {
if r >= '0' && r <= '9' {
return r
}
return -1
}, num)
// A number can be 11 digits long, if it starts with a 1
// but ignore that one.
if len(num) == 11 && num[0] == '1' {
num = num[1:]
}
if len(num) != 10 {
return "", errors.New("Invalid Number")
}
return num, nil
}
// AreaCode takes a number, validates it, and returns the area code
func AreaCode(num string) (string, error) {
num, err := Number(num)
if err != nil {
return "", err
}
return num[:3], nil
}
// Format takes a number, validates it and returns it in the format
// (nnn) nnn-nnnn
func Format(num string) (string, error) {
num, err := Number(num)
if err != nil {
return "", err
}
return "(" + num[:3] + ") " + num[3:6] + "-" + num[6:], nil
}

36
go/pig-latin/README.md Normal file
View File

@ -0,0 +1,36 @@
# Pig Latin
Implement a program that translates from English to Pig Latin
Pig Latin is a made-up children's language that's intended to be
confusing. It obeys a few simple rules (below), but when it's spoken
quickly it's really difficult for non-children (and non-native speakers)
to understand.
- **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to
the end of the word.
- **Rule 2**: If a word begins with a consonant sound, move it to the
end of the word, and then add an "ay" sound to the end of the word.
There are a few more rules for edge cases, and there are regional
variants too.
See <http://en.wikipedia.org/wiki/Pig_latin> for more details.
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
The Pig Latin exercise at Test First Teaching by Ultrasaurus [https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/](https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

41
go/pig-latin/igpay.go Normal file
View File

@ -0,0 +1,41 @@
package igpay
import "strings"
// PigLatin takes some words and converts them to pig latin
// 1) Move starting consonant sounds to the end of the word
// 2) add 'ay'
func PigLatin(in string) string {
var ret string
words := strings.Split(in, " ")
for i := range words {
ret += pigLatinWord(words[i])
if i < len(words)-1 {
ret += " "
}
}
return ret
}
// pigLatinWord actually does the heavy lifting
func pigLatinWord(in string) string {
var ret string
// First move all consonant sounds to the end
for i := 0; i < len(in); i++ {
// Edge consonant sound cases
if in[0] == 'q' && in[1] == 'u' {
in = in[2:] + string(in[0]) + string(in[1])
}
vowelPos := strings.IndexAny(in, "aeiou")
// If we have a y and another vowel, then y is a consonant
// also, x
if vowelPos == 0 || (vowelPos > 1 && (in[0] == 'y' || in[0] == 'x')) {
return in + "ay"
}
in = in[1:] + string(in[0])
}
return ret
}

View File

@ -0,0 +1,29 @@
package igpay
import "testing"
var tests = []struct{ pl, in string }{
{"appleay", "apple"},
{"earay", "ear"},
{"igpay", "pig"},
{"oalakay", "koala"},
{"airchay", "chair"},
{"eenquay", "queen"},
{"aresquay", "square"},
{"erapythay", "therapy"},
{"ushthray", "thrush"},
{"oolschay", "school"},
{"ickquay astfay unray", "quick fast run"},
{"ellowyay", "yellow"},
{"yttriaay", "yttria"},
{"enonxay", "xenon"},
{"xrayay", "xray"},
}
func TestPigLatin(t *testing.T) {
for _, test := range tests {
if pl := PigLatin(test.in); pl != test.pl {
t.Fatalf("PigLatin(%q) = %q, want %q.", test.in, pl, test.pl)
}
}
}

View File

@ -0,0 +1,48 @@
# Prime Factors
Compute the prime factors of a given natural number.
A prime number is only evenly divisible by itself and 1.
Note that 1 is not a prime number.
## Example
What are the prime factors of 60?
- Our first divisor is 2. 2 goes into 60, leaving 30.
- 2 goes into 30, leaving 15.
- 2 doesn't go cleanly into 15. So let's move on to our next divisor, 3.
- 3 goes cleanly into 15, leaving 5.
- 3 does not go cleanly into 5. The next possible factor is 4.
- 4 does not go cleanly into 5. The next possible factor is 5.
- 5 does go cleanly into 5.
- We're left only with 1, so now, we're done.
Our successful divisors in that computation represent the list of prime
factors of 60: 2, 2, 3, and 5.
You can check this yourself:
- 2 * 2 * 3 * 5
- = 4 * 15
- = 60
- Success!
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
The Prime Factors Kata by Uncle Bob [http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata](http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

21
go/prime-factors/prime.go Normal file
View File

@ -0,0 +1,21 @@
package prime
const testVersion = 2
func Factors(fnd int64) []int64 {
var ret []int64
var i int64
foundFactor := true
for i = 2; i < fnd; i++ {
for j := range ret {
if i%ret[j] == 0 {
// Already covered this case, move on
foundFactor = false
}
if !foundFactor {
break
}
}
}
return ret
}

View File

@ -0,0 +1,48 @@
package prime
// Return prime factors in increasing order
import (
"reflect"
"testing"
)
const targetTestVersion = 2
var tests = []struct {
input int64
expected []int64
}{
{1, []int64{}},
{2, []int64{2}},
{3, []int64{3}},
{4, []int64{2, 2}},
{6, []int64{2, 3}},
{8, []int64{2, 2, 2}},
{9, []int64{3, 3}},
{27, []int64{3, 3, 3}},
{625, []int64{5, 5, 5, 5}},
{901255, []int64{5, 17, 23, 461}},
{93819012551, []int64{11, 9539, 894119}},
}
func TestPrimeFactors(t *testing.T) {
if testVersion != targetTestVersion {
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
}
for _, test := range tests {
actual := Factors(test.input)
if !reflect.DeepEqual(actual, test.expected) {
t.Errorf("prime.Factors(%d) = %v; expected %v",
test.input, actual, test.expected)
}
}
}
func BenchmarkPrimeFactors(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range tests {
Factors(test.input)
}
}
}

53
go/strain/README.md Normal file
View File

@ -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.

54
go/strain/strain.go Normal file
View File

@ -0,0 +1,54 @@
package strain
// Ints is a slice of int
type Ints []int
// Lists is a slice of int slices
type Lists [][]int
// Strings is a slice of strings
type Strings []string
// Keep runs the Ints through a function and returns the ones that succeed
func (i Ints) Keep(kf func(int) bool) Ints {
var ret Ints
for idx := range i {
if kf(i[idx]) {
ret = append(ret, i[idx])
}
}
return ret
}
// Discard runs the Ints through a function and returns the ones that fail
func (i Ints) Discard(df func(int) bool) Ints {
var ret Ints
for idx := range i {
if !df(i[idx]) {
ret = append(ret, i[idx])
}
}
return ret
}
// Keep runs the Lists through a function and returns the ones that succeed
func (l Lists) Keep(kf func([]int) bool) Lists {
var ret Lists
for idx := range l {
if kf(l[idx]) {
ret = append(ret, l[idx])
}
}
return ret
}
// Keep runs the Strings through a function and returns the ones that succeed
func (s Strings) Keep(kf func(string) bool) Strings {
var ret Strings
for idx := range s {
if kf(s[idx]) {
ret = append(ret, s[idx])
}
}
return ret
}

170
go/strain/strain_test.go Normal file
View File

@ -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)
}
}
}