From a7d96f5106879c67e5b35b6df7d8d2385fe0246f Mon Sep 17 00:00:00 2001 From: Robert Jeutter Date: Wed, 12 Jan 2022 19:22:31 +0100 Subject: [PATCH] GO by example, part 5 --- 5-go-by-example/41-sorting.go | 26 ++++++ 5-go-by-example/42-sorting-by-functions.go | 27 +++++++ 5-go-by-example/43-panic.go | 16 ++++ 5-go-by-example/44-defer.go | 42 ++++++++++ 5-go-by-example/45-recover.go | 27 +++++++ 5-go-by-example/46-collection-function.go | 87 ++++++++++++++++++++ 5-go-by-example/47-string-functions.go | 31 ++++++++ 5-go-by-example/48-string-.formatting.go | 82 +++++++++++++++++++ 5-go-by-example/49-regex.go | 57 +++++++++++++ 5-go-by-example/50-json.go | 93 ++++++++++++++++++++++ 10 files changed, 488 insertions(+) create mode 100644 5-go-by-example/41-sorting.go create mode 100644 5-go-by-example/42-sorting-by-functions.go create mode 100644 5-go-by-example/43-panic.go create mode 100644 5-go-by-example/44-defer.go create mode 100644 5-go-by-example/45-recover.go create mode 100644 5-go-by-example/46-collection-function.go create mode 100644 5-go-by-example/47-string-functions.go create mode 100644 5-go-by-example/48-string-.formatting.go create mode 100644 5-go-by-example/49-regex.go create mode 100644 5-go-by-example/50-json.go diff --git a/5-go-by-example/41-sorting.go b/5-go-by-example/41-sorting.go new file mode 100644 index 0000000..9c07612 --- /dev/null +++ b/5-go-by-example/41-sorting.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "sort" +) + +// Go’s sort package implements sorting for builtins and user-defined types +func main() { + // Sort methods are specific to the builtin type + + // an example for strings + // sorting is in-place, so it changes the given slice and doesn’t return a new one + strs := []string{"c", "a", "b"} + sort.Strings(strs) + fmt.Println("Strings:", strs) + + // an example of sorting ints + ints := []int{7, 2, 4} + sort.Ints(ints) + fmt.Println("Ints: ", ints) + + // use sort to check if a slice is already in sorted order + s := sort.IntsAreSorted(ints) + fmt.Println("Sorted: ", s) +} diff --git a/5-go-by-example/42-sorting-by-functions.go b/5-go-by-example/42-sorting-by-functions.go new file mode 100644 index 0000000..803b918 --- /dev/null +++ b/5-go-by-example/42-sorting-by-functions.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "sort" +) + +// in order to sort by a custom function in Go, you need a corresponding type +type byLength []string + +// implement sort.Interface - Len, Less, and Swap - on the type +func (s byLength) Len() int { + return len(s) +} +func (s byLength) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s byLength) Less(i, j int) bool { + return len(s[i]) < len(s[j]) +} + +func main() { + // implement the custom sort by converting the original fruits slice to byLength and then use sort + fruits := []string{"peach", "banana", "kiwi"} + sort.Sort(byLength(fruits)) + fmt.Println(fruits) +} diff --git a/5-go-by-example/43-panic.go b/5-go-by-example/43-panic.go new file mode 100644 index 0000000..6e114e5 --- /dev/null +++ b/5-go-by-example/43-panic.go @@ -0,0 +1,16 @@ +package main + +import "os" + +// a panic typically means something went unexpectedly wrong +func main() { + + // this is the only code here designed to panic + panic("a problem") + + // a common use of panic is to abort if a function returns an error value that one doesn’t know how to handle + _, err := os.Create("/tmp/file") + if err != nil { + panic(err) + } +} diff --git a/5-go-by-example/44-defer.go b/5-go-by-example/44-defer.go new file mode 100644 index 0000000..e0dc88f --- /dev/null +++ b/5-go-by-example/44-defer.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "os" +) + +// Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup +func main() { + // suppose you wanted to create a file, write to it, and then close when done + // immediately after getting a file object with createFile, defer the closing of that file with closeFile + // this will be executed at the end of the enclosing function (main), after writeFile has finished + f := createFile("/tmp/defer.txt") + defer closeFile(f) + writeFile(f) +} + +func createFile(p string) *os.File { + fmt.Println("creating") + f, err := os.Create(p) + if err != nil { + panic(err) + } + return f +} + +func writeFile(f *os.File) { + fmt.Println("writing") + fmt.Fprintln(f, "data") + +} + +// it’s important to check for errors when closing a file, even in a deferred function +func closeFile(f *os.File) { + fmt.Println("closing") + err := f.Close() + + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff --git a/5-go-by-example/45-recover.go b/5-go-by-example/45-recover.go new file mode 100644 index 0000000..567b535 --- /dev/null +++ b/5-go-by-example/45-recover.go @@ -0,0 +1,27 @@ +package main + +import "fmt" + +// this function panics +func mayPanic() { + panic("a problem") +} + +// it is possible to recover from a panic, by using the recover built-in function +func main() { + + // recover must be called within a deferred function + // when the enclosing function panics, the defer will activate and a recover call within it will catch the panic + defer func() { + if r := recover(); r != nil { + // the return value of recover is the error raised in the call to panic + fmt.Println("Recovered. Error:\n", r) + } + }() + + mayPanic() + + // this code will not run, because mayPanic panics + // the execution of main stops at the point of the panic and resumes in the deferred closure + fmt.Println("After mayPanic()") +} diff --git a/5-go-by-example/46-collection-function.go b/5-go-by-example/46-collection-function.go new file mode 100644 index 0000000..073bc23 --- /dev/null +++ b/5-go-by-example/46-collection-function.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + "strings" +) + +// in Go it’s common to provide collection functions if and when they are specifically needed for your program and data types + +// Index returns the first index of the target string t, or -1 if no match is found +func Index(vs []string, t string) int { + for i, v := range vs { + if v == t { + return i + } + } + return -1 +} + +// Include returns true if the target string t is in the slice +func Include(vs []string, t string) bool { + return Index(vs, t) >= 0 +} + +// Any returns true if one of the strings in the slice satisfies the predicate f +func Any(vs []string, f func(string) bool) bool { + for _, v := range vs { + if f(v) { + return true + } + } + return false +} + +// All returns true if all of the strings in the slice satisfy the predicate f +func All(vs []string, f func(string) bool) bool { + for _, v := range vs { + if !f(v) { + return false + } + } + return true +} + +// Filter returns a new slice containing all strings in the slice that satisfy the predicate f +func Filter(vs []string, f func(string) bool) []string { + vsf := make([]string, 0) + for _, v := range vs { + if f(v) { + vsf = append(vsf, v) + } + } + return vsf +} + +// Map returns a new slice containing the results of applying the function f to each string in the original slice +func Map(vs []string, f func(string) string) []string { + vsm := make([]string, len(vs)) + for i, v := range vs { + vsm[i] = f(v) + } + return vsm +} + +func main() { + // try out various collection functions + var strs = []string{"peach", "apple", "pear", "plum"} + + fmt.Println(Index(strs, "pear")) + + fmt.Println(Include(strs, "grape")) + + fmt.Println(Any(strs, func(v string) bool { + return strings.HasPrefix(v, "p") + })) + + fmt.Println(All(strs, func(v string) bool { + return strings.HasPrefix(v, "p") + })) + + fmt.Println(Filter(strs, func(v string) bool { + return strings.Contains(v, "e") + })) + + fmt.Println(Map(strs, strings.ToUpper)) + +} diff --git a/5-go-by-example/47-string-functions.go b/5-go-by-example/47-string-functions.go new file mode 100644 index 0000000..b669204 --- /dev/null +++ b/5-go-by-example/47-string-functions.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + s "strings" +) + +// alias fmt.Println to a shorter name +var p = fmt.Println + +// standard library’s strings package provides many useful string-related functions +func main() { + // sample of the functions available in strings + p("Contains: ", s.Contains("test", "es")) + p("Count: ", s.Count("test", "t")) + p("HasPrefix: ", s.HasPrefix("test", "te")) + p("HasSuffix: ", s.HasSuffix("test", "st")) + p("Index: ", s.Index("test", "e")) + p("Join: ", s.Join([]string{"a", "b"}, "-")) + p("Repeat: ", s.Repeat("a", 5)) + p("Replace: ", s.Replace("foo", "o", "0", -1)) + p("Replace: ", s.Replace("foo", "o", "0", 1)) + p("Split: ", s.Split("a-b-c-d-e", "-")) + p("ToLower: ", s.ToLower("TEST")) + p("ToUpper: ", s.ToUpper("test")) + p() + + // not part of strings + p("Len: ", len("hello")) + p("Char:", "hello"[1]) +} diff --git a/5-go-by-example/48-string-.formatting.go b/5-go-by-example/48-string-.formatting.go new file mode 100644 index 0000000..55ea2b6 --- /dev/null +++ b/5-go-by-example/48-string-.formatting.go @@ -0,0 +1,82 @@ +package main + +import ( + "fmt" + "os" +) + +type point struct { + x, y int +} + +// common string formatting tasks +func main() { + // prints an instance of point struct + p := point{1, 2} + fmt.Printf("struct1: %v\n", p) + + // the %+v variant will include the struct’s field names + fmt.Printf("struct2: %+v\n", p) + + // the %#v variant prints a Go syntax representation of the value + fmt.Printf("struct3: %#v\n", p) + + // the %T variant prints the type of a value + fmt.Printf("type: %T\n", p) + + // formatting booleans straight-forward + fmt.Printf("bool: %t\n", true) + + // use %d for standard, base-10 formatting + fmt.Printf("int: %d\n", 123) + + // prints a binary representation + fmt.Printf("bin: %b\n", 14) + + // prints the character corresponding to the given integer + fmt.Printf("char: %c\n", 33) + + // %x provides hex encoding + fmt.Printf("hex: %x\n", 456) + + // decimal formatting use %f + fmt.Printf("float1: %f\n", 78.9) + + // %e and %E format the float in (different versions of) scientific notation + fmt.Printf("float2: %e\n", 123400000.0) + fmt.Printf("float3: %E\n", 123400000.0) + + // basic string printing use %s + fmt.Printf("str1: %s\n", "\"string\"") + + // to double-quote strings use %q + fmt.Printf("str2: %q\n", "\"string\"") + + // %x renders the string in base-16, with two output characters per byte of input + fmt.Printf("str3: %x\n", "hex this") + + // use %p to print a representation of a pointer + fmt.Printf("pointer: %p\n", &p) + + // to specify the width of an integer, use a number after the % in the verb + fmt.Printf("width1: |%6d|%6d|\n", 12, 345) + + // to specify the width of printed floats + fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45) + + // to left-justify, use the - flag + fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45) + + // specify width when formatting strings + fmt.Printf("width4: |%6s|%6s|\n", "foo", "b") + + // and left-justify strings with - too + fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b") + + // Sprintf formats and returns a string without printing it anywhere + s := fmt.Sprintf("sprintf: a %s", "string") + fmt.Println(s) + + // format+print to io.Writers other than os.Stdout using Fprintf + fmt.Fprintf(os.Stderr, "io: an %s\n", "error") +} diff --git a/5-go-by-example/49-regex.go b/5-go-by-example/49-regex.go new file mode 100644 index 0000000..b036c13 --- /dev/null +++ b/5-go-by-example/49-regex.go @@ -0,0 +1,57 @@ +package main + +import ( + "bytes" + "fmt" + "regexp" +) + +// Go offers built-in support for regular expressions +func main() { + // test whether a pattern matches a string + match, _ := regexp.MatchString("p([a-z]+)ch", "peach") + fmt.Println(match) + + // compile an optimized Regexp struct + r, _ := regexp.Compile("p([a-z]+)ch") + + // match test like earlier + fmt.Println(r.MatchString("peach")) + + // finds the match for the regex + fmt.Println(r.FindString("peach punch")) + + // finds the first match but returns the start and end indexes for the match instead of the matching text + fmt.Println("idx:", r.FindStringIndex("peach punch")) + + // include information about both the whole-pattern matches and the submatches within those matches + fmt.Println(r.FindStringSubmatch("peach punch")) + + // information about the indexes of matches and submatches + fmt.Println(r.FindStringSubmatchIndex("peach punch")) + + // apply to all matches in the input, not just the first + fmt.Println(r.FindAllString("peach punch pinch", -1)) + + // the All variants are available for the other functions too + fmt.Println("all:", r.FindAllStringSubmatchIndex( + "peach punch pinch", -1)) + + // providing a non-negative integer as the second argument to these functions will limit the number of matches + fmt.Println(r.FindAllString("peach punch pinch", 2)) + + // provide []byte arguments and drop String from the function name + fmt.Println(r.Match([]byte("peach"))) + + // MustCompile panics instead of returning an error, which makes it safer to use for global variables + r = regexp.MustCompile("p([a-z]+)ch") + fmt.Println("regexp:", r) + + // replace subsets of strings with other values + fmt.Println(r.ReplaceAllString("a peach", "")) + + // transform matched text with a given functions + in := []byte("a peach") + out := r.ReplaceAllFunc(in, bytes.ToUpper) + fmt.Println(string(out)) +} diff --git a/5-go-by-example/50-json.go b/5-go-by-example/50-json.go new file mode 100644 index 0000000..d2bb9b2 --- /dev/null +++ b/5-go-by-example/50-json.go @@ -0,0 +1,93 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" +) + +// demonstrate encoding and decoding of custom types below +type response1 struct { + Page int + Fruits []string +} + +// only exported fields will be encoded/decoded in JSON +// Fields must start with capital letters to be exported +type response2 struct { + Page int `json:"page"` + Fruits []string `json:"fruits"` +} + +// Go offers built-in support for JSON encoding and decoding, including to and from built-in and custom data types +func main() { + // encoding basic data types to JSON strings + bolB, _ := json.Marshal(true) + fmt.Println(string(bolB)) + + intB, _ := json.Marshal(1) + fmt.Println(string(intB)) + + fltB, _ := json.Marshal(2.34) + fmt.Println(string(fltB)) + + strB, _ := json.Marshal("gopher") + fmt.Println(string(strB)) + + // some for slices and maps, which encode to JSON arrays and objects + slcD := []string{"apple", "peach", "pear"} + slcB, _ := json.Marshal(slcD) + fmt.Println(string(slcB)) + + mapD := map[string]int{"apple": 5, "lettuce": 7} + mapB, _ := json.Marshal(mapD) + fmt.Println(string(mapB)) + + // the JSON package can automatically encode your custom data types + // it will only include exported fields in the encoded output and will by default use those names as the JSON keys + res1D := &response1{ + Page: 1, + Fruits: []string{"apple", "peach", "pear"}} + res1B, _ := json.Marshal(res1D) + fmt.Println(string(res1B)) + + // use tags on struct field declarations to customize the encoded JSON key names + res2D := &response2{ + Page: 1, + Fruits: []string{"apple", "peach", "pear"}} + res2B, _ := json.Marshal(res2D) + fmt.Println(string(res2B)) + + // generic decoding JSON data into Go values + byt := []byte(`{"num":6.13,"strs":["a","b"]}`) + + // provide a variable where the JSON package can put the decoded data + var dat map[string]interface{} + + // actual decoding, and a check for associated errors + if err := json.Unmarshal(byt, &dat); err != nil { + panic(err) + } + fmt.Println(dat) + + // in order to use the values in the decoded map, convert them to their appropriate type + num := dat["num"].(float64) + fmt.Println(num) + + // accessing nested data requires a series of conversions + strs := dat["strs"].([]interface{}) + str1 := strs[0].(string) + fmt.Println(str1) + + // decode JSON into custom data types + str := `{"page": 1, "fruits": ["apple", "peach"]}` + res := response2{} + json.Unmarshal([]byte(str), &res) + fmt.Println(res) + fmt.Println(res.Fruits[0]) + + // stream JSON encodings directly to os.Writers + enc := json.NewEncoder(os.Stdout) + d := map[string]int{"apple": 5, "lettuce": 7} + enc.Encode(d) +}