# Gopl 筆記 part2 [TOC] ## 5. Functions ### [Findlinks 解析](/rEz5vxg0Qq2L4PIZqsoHpw) ### [outline 解析](/FBN5V0XWQeWqCIZKXj7R8g) ### A tour of go #### [Function values](https://tour.golang.org/moretypes/24) * my practice: * implement a function that compute the relationship of two values with appropriate handle function #### Function closures * Why do we use closure? * :::success TODO #### [Function closures](https://tour.golang.org/moretypes/25) #### [Exercise: Fibonacci closure](https://tour.golang.org/moretypes/26) ::: ### [匿名函式與閉包](https://openhome.cc/Gossip/Go/Closure.html) * :question: * :question: How to implement a given function that filter the slice by given rules? :::spoiler ```go= // Define the type of handling function of filter type Rule func(int) bool // filter filter the given slice with handling predicate func filter(origin []int, rule Rule) []int { out := []int{} for _, elem := range origin { if rule(elem) { out = append(out, elem) } } return out } // Usage: func main() { data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} fmt.Println("Input slice: ", data) // Pass in data and the handling function to filter // Filter the slice by elements less than 5 fmt.Println("Filter by number that is less than 5") fmt.Println(filter(data, func(elem int) bool { return elem < 5 })) } ``` ::: * :question: * 閉包的意義是? * 閉包將變數本身關閉在自己的範疇中 * references variables declared outside of the function itself. * 可以不通過 argument or return value 傳遞變數到對應的 handler * 舉例說明 * [axis getter and setter](https://github.com/unknowntpo/sandbox/commit/87bc465fe58fc4e9c11f6d1d56b3dd1c4107a519) * TODO: 找 closure 的應用 * [一級函式](https://openhome.cc/Gossip/Go/FirstClassFunction.html) ### 5.1 Function Definitions * blank identifier * omit unused variable * :question: What if we want to ignore all of return values? * [go playground](https://play.golang.org/p/yeoFkHAx4b4) * passed by value * passed by reference ### 5.2 Recursion * 前情提要 (gopl 4.4 bst tree) * findlinks1 * html document tree (BST) * The main function parses the standard input as HTML, extracts the links using a recursive `visit` function, and prints each discovered link * outline * fetch ### 5.3 Multiple return values * findlinks2 * no longer need to run fetch first * ### 5.4 Errors #### 5.4.1 Error-Handling Strategies * Take `findLinks` as example * example: `wait` * 五種 error handling stratagies? * 1. Propagate the error * fail in subrutine become failure of calling routine ```go resp, err := http.Get(url) // subroutine: http.Get() if err != nil { ... } ``` * :question: * What does `fmt.Errorf()` do? * format an error using `fmt.Sprintf()` * return a new error value * (so it can be concatinate!) * Error 串接方法: * 一率從 prefix 開始新增新的 error * 2. Retry with given timeout * e.g. [wait](https://github.com/adonovan/gopl.io/tree/master/ch5/wait) #### 5.4.2 End of File (EOF) see `charcount` program in `ch4.3 Maps` ### 5.5 Function values * :question: What does Function values mean? * [A Tour of Go](https://tour.golang.org/moretypes/24#:~:text=Functions%20are%20values%20too.,function%20arguments%20and%20return%20values.) * :question: What can function value do? * Function values may be used as function arguments and return values. * :question: What does it meas `first-class values` ? (p.257) ```go= func square(n int) int {return n * n} f := square // f is function value ``` * example `outline2` * show HTML DOM Tree with leveled structure ### 5.6 Anonymous Functions * :question: What is anonymous function? :::spoilerr * Diff between `Anonymous function` and `Named functions` * `Named functions` * which can be declared at package level * have `func` keyword + function name * `Anonymous functions` * no `func` keyword + function name * :question: How to declare? * use function literal * its value is called `anonymous function` ::: * function literal * e.g. `squares`: return a the next square number each time it is called * `e.g.toposort` * `e.g. findLinks3` * e.g. `links` #### 5.6.1 Caveat: Capturing Iteration Variables ### 5.7 Variadic Functions * e.g. `sum` * e.g. `errorf` ### 5.8 Deferred Function Calls * e.g. `title1`: output the title of given website * e.g. `titles2` * e.g. `ioutil` * e.g. in 9.2, used `defer` unlock mutex * e.g. improved `fetch` program (ch1.5) ### 5.9 Panic * :question: What does it do? * Capture runtime-error * e.g. out-of bound array access * nil pointer reference. * e.g. `regexp` package: regexp.MustCompile() * e.g. `defer1` * e.g. `defer2` ### 5.10 Recover * :question: What job does recover do? * recover from panic * e.g. `title3` ::: ### Defer, Panic, and Recover * :question: How to use defer and recover to recover from panic? :::spoiler ```go package main import "fmt" func main() { fmt.Println("Panic is comming (❁°͈▵°͈)") defer func() { if r := recover(); r != nil { // Display the panic message fmt.Println("What's happended?", r) fmt.Println("Recover from panic finally ...") } else { // No panic, nothing to recover from fmt.Println("Nothing happened, don't worry!") } }() panic("Panic") } ``` ### [An Introduction to Programming in go](https://www.golang-book.com/books/intro): #### [Functions](https://www.golang-book.com/books/intro/7) * :question: What forms a closure? * :question: What is panic? * :question: How to use recover and defer to recover from panic? * [Comit: Using defer and panic to recover from panic](https://github.com/unknowntpo/sandbox/commit/86ecf9bef8cd0a326f85b6f45c118c93d205f918) * :question: **When does the defer function been executed?** * Ans: [The Go blog - Defer, Panic, and Recover](https://blog.golang.org/defer-panic-and-recover) :::spoiler > # The Go Blog: Defer, Panic, and Recover > A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions. :::success * 也就是 `defer` 關鍵字所在的 function return 前,會把 使用到 defer 關鍵字的 function 以 LIFO 的方式一一呼叫,最後才會 return ::: * :question: **How to use defer function to clean up resources?** * Ans:[Commits: Using defer to handle cleanup function](https://github.com/unknowntpo/sandbox/commit/bd42312d2f59bc6add88c1b834122029ecfa999e) :::success 施工中 :construction: ## 6. Methods * Intro * brief * OOP * encapsulation * composition * relationship between objrect, method ### 6.1 Method Declarations * :question: What is a receiver? (p.297) * example: geometry package (gopl/ch6/geometry) * [experiment: pointer receiver addresss](https://play.golang.org/p/KFw2jzRbofe) * :question: What's the diff between func, method call? * :question: What is selector? * can it be used in struct? * :question: How to use package write by myself? * see ch10. go tool and package ### 6.2 Methods with a pointer receivor * :question: Why use pointers as receiver? * Don't want to copy big data structure ::: :::danger Pointer receiver 只是把 object 的位置傳給 receiver 而已, 當 `r == nil` 時 `r.Insert(5)` 並不會更新 r 所指向的位置,所以 insert 後 r仍然指向 nil ::: * :question: What's the diff between named type and pointer type? * Can we use the same name in named type and pointer type in the same time? * :information_source: 講到許多 pointer 在 method 的用法,包含 pointer to pointer #### 6.2.1 Nil is a Valid Receiver Value * 舉 `list == nil` 為例來說明,把 list 當 object 操作時,對他做 nil list 的比較的正確性 * e.g. `net/url` package ### 6.3 Composite types by struct embedding ## 7. Interface ### Go by example * :dart: Implement geometry measuring function * take an interface **geometry** as input * print the area and perimeter of that geometry * :question: How to use a function get multiple different method as input at the same time? * using interface * geometry (interface) * rect (object) * rect.area (method) * rect.perim (method) * circle (object) * circle.area (method) * circle.perim (method) * Answer: [commits](https://github.com/unknowntpo/sandbox/commit/12580e589059fad1cd6dc7b78d86936a1fa536ed) --- ### A tour of Go * [interface](https://tour.golang.org/methods/9) * [errors](https://tour.golang.org/methods/20) * :question: Why these code get * ./prog.go:14:28: cannot use ErrNegativeSqrt(-2) (type ErrNegativeSqrt) as type error in return argument: ErrNegativeSqrt does not implement error (Error method has pointer receiver) * [play](https://play.golang.org/p/JMOI80P26bf) * ANs: 與 A tour of go - 19 比對就知道了! * gopl p.335 有講到 * It is legal to call a *T method on an argument of type T so long as the argument is a variable; the compiler implicitly takes its address. * [play](https://play.golang.org/p/HqLBqaj0GF8) ### GOPL * :question: * What does it mean "go's interfaces are satisfied implicitly"? (p.325) * What is the type of interface type? * abstract type * like queue, stack * unknown implementation * Draw the relationship between * `fmt.Fprintf()` * `fmt.Printf()` * `fmt.Sprintf()` * What can be the concrete type of `io.Writer()`? * `*os.File` * `*bytes.Buffer` * Re: gopl - ByteCounter * [fmt.Stringer 解析](/cryNbGbHS4ONqZcbQMnugQ) * [Exercise 7.4 - NewReader 解析](/B-SjSgvLQra20nvpgvBcnQ) :::success TODO: ::: ### 7.3 Interface Satisfaction * :question: What is interface satisfaction? * a type satisfiy an interface, when that type has all method defined in interface :::success TODO: 實作後嘗試了解這些 * p.335: `T`, `*T` receiver 問題 * s, &s 都可以 使用 .String() method * 但是 只有 `&s` 可以 assign 給 type 為 fmt.Stringer 的 變數 * 因為只有 `&s` 擁有 String() method * p.336: * p.337: Type Assertion: * interface get it's concrete type by type assertion ::: ## 8. Goroutines and Channels ### Go by example * :question: * Does go routine run in same address space? * Yes, * ref: a tour of go * How to pass in args to anonymous function? :::success TODO: Wait for all goroutines to finish (for a more robust approach, use a [WaitGroup](https://gobyexample.com/waitgroups)). ::: ### gopl #### example: Spinner * :question: What does `\r` do? * Ref: [Stack overflow](https://stackoverflow.com/questions/7372918/whats-the-use-of-r-escape-sequence#:~:text=8%20Answers&text=%5Cr%20is%20a%20carriage%20return,line%20of%20the%20terminal%20emulator.) * :question: What is the difference betwen back quote (\`\`) and double quote ("") ? * single quote (\'\'): * define a rune or byte * e.g. * double quote (""): * define a string * it will honor any escape character * back quote (\`\`): * define a string * without any escaping * ref: [go by example - Double, Single and Back quotes in Go (Golang) ](https://golangbyexample.com/double-single-back-quotes-go/) * :question: How to print a spinner with delay specified? * :question: Why does spinner() stop ? Isn't it infinite loop? ### example: clock1 - sequential Clock Server #### control flow of clock1 * main * listener, err = net.Listen() * conn, err = listener.Accept() * handleConn(conn) * io.WriteString(conn, time.Now().Format()) :::success TODO: 了解 clock1 behavior 回答問題 ::: ### example: netcat1 - go version of `nc` (netcat) -- arbitrary TCP and UDP connections and listens * :question: What isthe different between TCP & HTTP? * :question: clock1 為什麼不能用 browser access 呢? * :question: What's the different between clock1 & clock2? * :question: conn 代表什麼?往 conn 做 WriteString() 的意義是? ### example: reverb1 * :question: workflow? ```graphviz digraph { rankdir=LR conn input echo conn -> input [label="bufio.NewScanner()"] input -> echo [label="input.Scan()"] echo ->conn [label="echo(conn, shout, delay)"] } ``` * :question: Trivial version 的 echo (p. 424) `io.Copy(dst, src)` 不能拿來修改輸入嗎?一定要用 reverb1 的方法? * 差別在 buffer? * :question: reverb1 與 clock1 的 handleConn() 差異是? * :question: 有哪些對 io.Reader io.Writter 做讀寫的方法? 差異是? * Write * `os.Write()` * `io.WriteString()` * Read * `bufio.Scanner.Scan()` * `os.Read()` * `ioutil.ReadAll(r io.Reader)` * :question: What is a `bufio.Scanner` ? ### 8.4 Channels * :question: Can channel be compared to * 1. another channel * 2. nil ? :::spoler 1. Yes, it can be compare with the channel of same element type 2. Yes, because the zero value of a channel is nil ::: * :question: What are the channel operations? * send * receive * close #### 8.4.1 Unbuffered channel (Synchronized channel) * :question: In unbuffered channel, will it be blocked? explain the behavior of blocking * Recap: a sender goroutine and receiver goroutine communicate through a channel :::spoiler * 1. if sender execute send operation first, * sender will be blocked until receiver execute receive operation. * 2. if receiver execute receive operation first, * receiver will be blocked until sender execute send operation. ::: :::success * :bulb: Communicate over an unbuffered channel causes the sending and receiving goroutines to '*Synchronize*' ::: * :question: What is a buffered channel in golang? * a FIFO queue * >Channels act as first-in-first-out queues. * Ref: [go spec](https://golang.org/ref/spec#Channel_types) * :question: `netcat2` v.s. `netcat3`? * if `mustCopy(conn, os.Stdin)` are done, the main goroutine will stop, * it doesn't wait for `go mustCopy(os.Stdout, conn)` * :question: Why send `struct{}` not `bool`? ### [Go by Example: Channel Synchronization](https://gobyexample.com/channel-synchronization) * :question: How to use a channel to wait until a job is done? ### [Go by Example: Channel Directions](https://gobyexample.com/channel-directions) * Two type of channel direction * Send-only (`chan<-`) * we can only send data to a channel * Receive-only (`<-chan`) * we can only receive data from a channel * What if we send data to receive-only channel * Get compile-time error * `./main.go:13:8: invalid operation: pings <- "a" (send to receive-only type <-chan string)` ### [Go by Example: Select](https://gobyexample.com/select) * :question: How to wait for multiple goroutines to finish its job using channel and `select`? <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> ## 11. Testing ### [profillingo: Basics of benchmarking, profiling and tracing with Go](https://github.com/samonzeweb/profilinggo) ### Testing - Go by Example Ref: [Go by Example: Testing](https://gobyexample.com/testing) * Given * main.go * We wanna test `IntMin()` * Try to write tableDrive testing * :question: What's is the constrain of test file name and function name? :::spoiler E.g. we wanna test main.go Must contain *_test.go in test file name ::: * :question: What is the field of structure `testing.T`? * Sample code: * `test_main.go` :::spoiler ```go package main import ( "fmt" "testing" ) // Hard-coded testing function func TestIntMinBasic(t *testing.T) { // what does testing.T means? ans := IntMin(2, -2) if ans != -2 { t.Errorf("IntMin(2, -2) = %d; want -2", ans) } } // Prefered testing function func TestIntMinTableDriven(t *testing.T) { var tests = []struct { a, b int want int }{ {0, 1, 0}, {1, 0, 0}, {2, -2, -2}, {0, -1, -1}, {-1, 0, -1}, } for _, tt := range tests { testname := fmt.Sprintf("%d %d", tt.a, tt.b) // what does Sprintf() do t.Run(testname, func(t *testing.T) { ans := IntMin(tt.a, tt.b) if ans != tt.want { t.Errorf("IntMin(%d, %d) = %d; want %d", tt.a, tt.b, ans, tt.want) } }) } } ``` ::: * Output: :::spoiler ```shell $ go test -v === RUN TestIntMinBasic --- PASS: TestIntMinBasic (0.00s) === RUN TestIntMinTableDriven === RUN TestIntMinTableDriven/0_1 === RUN TestIntMinTableDriven/1_0 === RUN TestIntMinTableDriven/2_-2 === RUN TestIntMinTableDriven/0_-1 === RUN TestIntMinTableDriven/-1_0 --- PASS: TestIntMinTableDriven (0.00s) --- PASS: TestIntMinTableDriven/0_1 (0.00s) --- PASS: TestIntMinTableDriven/1_0 (0.00s) --- PASS: TestIntMinTableDriven/2_-2 (0.00s) --- PASS: TestIntMinTableDriven/0_-1 (0.00s) --- PASS: TestIntMinTableDriven/-1_0 (0.00s) PASS ok _/Users/tacomaker/repo/sandbox/go/testing 0.005s ``` ::: * :question:` t.Run()` 作用是? * :question:`t.Errorf()` 作用是? * :question: `t.Run()` 為什麼要傳入整個 function 當作參數呢? use showInfo() while first create playlist get error ```shell Creating playlist... panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1094e80] goroutine 1 [running]: main.(*playlist).showInfo(0xc00007af68) /Users/tacomaker/repo/sandbox/go/struct/linked_list_in_go/main.go:89 +0x30 main.main() /Users/tacomaker/repo/sandbox/go/struct/linked_list_in_go/main.go:99 +0xfc ``` ### 11.1 The go test tool * :question: What are three type of function within `*_test.go` files treated specially? :::spoiler * tests * function name: prefix `Test` * go test report the result * `PASS` or `FAIL` * benchmarks * function name: prefix `Benchmark` * go test report the mean execution time of the operation * examples * machine-checked documentation * function name: prefix `Example` ::: ### 11.2 Test functions #### word1.go * Goal: test our function `IsPalindrome()` in `word.go` * Our target function: :::spoiler ```go // IsPalindrome reports whether s reads the same forward and backward. // (Our first attempt.) func IsPalindrome(s string) bool { for i := range s { if s[i] != s[len(s)-1-i] { return false } } return true } ``` ::: * Test suit: * Expected `PASS` * `detartrated` * `kayak` * `été` ==**-->:interrobang: We got error here! it doesn't passed the test !**== * Expected `FAIL` * `palindrome` * `A man, a plan, a canal: Panama` * :question: Why `été` doesn't passed the test? :::spoiler Because `IsPalindrome()` used ==byte sequence==, not ==rune sequences!== so that non-ASCII characters such as the `é` in `été` confuse it. ::: <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br> * :question: How to run go test with specific test function? :::spoiler ```shell $ go test -run = "French|Canal" # regexp ``` ::: * :question: How to init an array of struct with `array literals`? :::spoiler * just like declarating an array, * line 3 - 6 specify the data type of element in array `tests` ```go=1 // just like declarating an array var a = [3]int{1, 2, 3} var tests = []struct { a, b int want int }{ {2, -2, -2}, {2, 3, 2}, {6, 10, 6}, {2, -7, -7}, {200, -100, -100}, } ``` ::: * :question: Why we can omit type of `test` in var declaration? * [Composite literal](https://golang.org/ref/spec#Composite_literals) * :information_source: Test function 中 prefix `Test` 之後的第一個字母也要大寫 * 就算你的待測試 function 叫做 `createPlaylist()` 也一樣 * 正確範例: * Function to test: `createPlayList()` * Test function: `TestCreatePlayList()` * 錯誤範例: * Test function:`TestcreatePlayList()` * 不然會出現 warning * `“testing: warning: no tests to run”` * Ref: [testing](https://golang.org/pkg/testing/) * :question: Why can we omit type in var declaration in test table? * 可以參考 6.3 Composing Types by Struct Embedding ### 11.2.1 Randomized Testing ### 11.2.2 Testing a command ### 11.4 Benchmark Functions ### [如何在 Go 語言內寫效能測試](https://blog.wu-boy.com/2018/06/how-to-write-benchmark-in-go/) * :question: * What does `testing.B.N` mean? * How to run benchmark without testing? * `$ go test -bench=. -run=none -benchmem .` * `-bench=.` * ref: `go help testflag` * Quote: To run all benchmarks, use '-bench .' or '-bench=.'. * `-run=none` * `-benchmem` * `.`