.. highlight:: go functions1.go ============= :: // functions1.go // To run at the command line: // $ go run functions1.go package main import "fmt" // returns the first index i such that match(s[i]) is false // if no character matches, then len(s) is returned func firstNonMatching(s string, match func(rune) bool) int { i := 0 for i < len(s) && match(rune(s[i])) { i++ } return i } func getLongestPrefix(s string, match func(rune) bool) string { return s[:firstNonMatching(s, match)] } func removeLongestPrefix(s string, match func(rune) bool) string { return s[firstNonMatching(s, match):] } func test_getLongestPrefix() { // This is an example of table-driven testing. // All the test cases are put in a slice of structs called testcases. // Each entry in testcases stores the input and expected output. // The code after the table then calls the function being tested, in // this case getLongestPrefix, on each entry in test cases, check that // the the expected output matches the actual output. // // Table-driven testing has a couple of nice features. It's automated, so // you can easily run it after any change to your code. It's also easy to // add new entires to testcases. testcases := []struct { str string matchFn func(rune) bool expected string }{ {"x+y", isLetter, "x"}, {"13 * j", isLetter, ""}, {"13 * j", isDigit, "13"}, {"one two three", isLetter, "one"}, } failures := 0 for i, test := range testcases { if actual := getLongestPrefix(test.str, test.matchFn); actual != test.expected { fmt.Printf("test %v failed:\n", i) fmt.Printf(" input: \"%v\"\n", test.str) fmt.Printf(" actual: \"%v\"\n", actual) fmt.Printf(" expected: \"%v\"\n\n", test.expected) failures++ } } if failures == 0 { fmt.Println("all tests passed") } else if failures == 1 { fmt.Println("1 test case failed") } else { fmt.Printf("%v test cases failed\n", failures) } } // test_getLongestPrefix func main() { test_getLongestPrefix() } // // some character matching functions // func isWhitespace(r rune) bool { return r == ' ' || r == '\n' || r == '\t' } func isLower(r rune) bool { return 'a' <= r && r <= 'z' } func isUpper(r rune) bool { return 'A' <= r && r <= 'Z' } func isLetter(r rune) bool { return isLower(r) || isUpper(r) } func isIdentStartChar(r rune) bool { return r == '_' || isLetter(r) } func isDigit(r rune) bool { return '0' <= r && r <= '9' } func isIdentChar(r rune) bool { return isIdentStartChar(r) || isDigit(r) }