GO by example, part 5

This commit is contained in:
Robert Jeutter 2022-01-12 19:22:31 +01:00
parent 5d7e654fa1
commit a7d96f5106
10 changed files with 488 additions and 0 deletions

View File

@ -0,0 +1,26 @@
package main
import (
"fmt"
"sort"
)
// Gos 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 doesnt 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)
}

View File

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

View File

@ -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 doesnt know how to handle
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}

View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"os"
)
// Defer is used to ensure that a function call is performed later in a programs 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")
}
// its 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)
}
}

View File

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

View File

@ -0,0 +1,87 @@
package main
import (
"fmt"
"strings"
)
// in Go its 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))
}

View File

@ -0,0 +1,31 @@
package main
import (
"fmt"
s "strings"
)
// alias fmt.Println to a shorter name
var p = fmt.Println
// standard librarys 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])
}

View File

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

View File

@ -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", "<fruit>"))
// transform matched text with a given functions
in := []byte("a peach")
out := r.ReplaceAllFunc(in, bytes.ToUpper)
fmt.Println(string(out))
}

View File

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