diff --git a/challenge_10/go/erocs/README.md b/challenge_10/go/erocs/README.md new file mode 100644 index 000000000..7c15b283a --- /dev/null +++ b/challenge_10/go/erocs/README.md @@ -0,0 +1,21 @@ +# Challenge 10: Match Closers + +## 1. Solution Description + +Create a pushdown atomata (utilizing a stack) to verify character group opening +characters are matched with appropriate closing characters in a given string. +Supported character sets: + + Opener Closer + ( ) + [ ] + { } + < > + +## 2. Running Tests + +In bash in this directory: + + export GOPATH=`pwd` + go get challenge10 + go test challenge10 diff --git a/challenge_10/go/erocs/src/challenge10/challenge10.go b/challenge_10/go/erocs/src/challenge10/challenge10.go new file mode 100644 index 000000000..342165156 --- /dev/null +++ b/challenge_10/go/erocs/src/challenge10/challenge10.go @@ -0,0 +1,37 @@ +package challenge10 + +import ( + lane "gopkg.in/oleiade/lane.v1" +) + +var openers map[rune]rune = map[rune]rune{ + '(': ')', + '[': ']', + '{': '}', + '<': '>', +} + +var closers map[rune]rune + +// Initialize package private variables. +func init() { + closers = map[rune]rune{} + for k, v := range openers { + closers[v] = k + } +} + +func HasValidClosers(s string) bool { + stk := lane.NewStack() + for _, ch := range s { + if closer, ok := openers[ch]; ok { + stk.Push(closer) + } else if !stk.Empty() && ch == stk.Head().(rune) { + stk.Pop() + } else if _, ok := closers[ch]; ok { + // Closer without an opener + return false + } + } + return stk.Empty() +} diff --git a/challenge_10/go/erocs/src/challenge10/challenge10_test.go b/challenge_10/go/erocs/src/challenge10/challenge10_test.go new file mode 100644 index 000000000..a5e46aa97 --- /dev/null +++ b/challenge_10/go/erocs/src/challenge10/challenge10_test.go @@ -0,0 +1,102 @@ +package challenge10 + +import ( + "testing" +) + +func TestNoClosers(t *testing.T) { + if !HasValidClosers("The quack brown fax jumped over the dazy dog.") { + t.Fail() + } +} + +func TestSingleCloser(t *testing.T) { + if !HasValidClosers("(Y)") { + t.Fail() + } +} + +func TestNoOpener(t *testing.T) { + if HasValidClosers("hi)") { + t.Fail() + } +} + +func TestNoCloser(t *testing.T) { + if HasValidClosers("(hi") { + t.Fail() + } +} + +func TestParens(t *testing.T) { + if !HasValidClosers("()") { + t.Error("Bad valid order") + } + if HasValidClosers(")(") { + t.Error("Bad invalid order") + } +} + +func TestBrackets(t *testing.T) { + if !HasValidClosers("[]") { + t.Error("Bad valid order") + } + if HasValidClosers("][") { + t.Error("Bad invalid order") + } +} + +func TestBraces(t *testing.T) { + if !HasValidClosers("{}") { + t.Error("Bad valid order") + } + if HasValidClosers("}{") { + t.Error("Bad invalid order") + } +} + +func TestLtGt(t *testing.T) { + if !HasValidClosers("<>") { + t.Error("Bad valid order") + } + if HasValidClosers("><") { + t.Error("Bad invalid order") + } +} + +func TestValidNesting(t *testing.T) { + if !HasValidClosers("{[(<>)[<<{}>>]]}") { + t.Fail() + } +} + +func TestInvalidNesting(t *testing.T) { + if HasValidClosers("({[<)}]>") { + t.Fail() + } +} + +func TestDeepNesting(t *testing.T) { + l := 20000 + rs := make([]rune, 0, l*2) + for i := 1; i <= l; i++ { + fizz := i%3 == 0 + buzz := i%5 == 0 + if fizz && buzz { + rs = append(rs, '(') + } else if fizz { + rs = append(rs, '[') + } else if buzz { + rs = append(rs, '{') + } else { + rs = append(rs, '<') + } + } + for i := l - 1; i >= 0; i-- { + rs = append(rs, openers[rs[i]]) + } + s := string(rs) + if !HasValidClosers(s) { + t.Fail() + } +}