Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Tour of Go 學習筆記

1,362 views

Published on

這是我自己依照 A Tour of Go 學習 Go 程式語言時的整理筆記。

Published in: Technology, Education
  • Be the first to comment

A Tour of Go 學習筆記

  1. 1. meebox@gmail.com 請實際上網搭配本投影片使用 http://tour.golang.org
  2. 2. package main import ( "fmt" "math/rand" ) func main() { fmt.Println("My favorite number is", rand.Intn(10)) } 程式由 main package 執行 匯入要使用的 package import "fmt" import "math/rand" 也可 寫成
  3. 3. 參數 說明 %f 用十進位小數表示浮點數 %e 用科學記號表示浮點數 %E 同上, 用大寫 E 表示科學記號 %g 取 %e 與 %f 中較簡短的結果 %G 取 %E 與 %f 中較簡短的結果 %T 顯示資料型別 %v 以型別預設的格式顯示資料值 %s 字串 %d 十進位整數 %q 為字串加上雙引號及適當跳脫字元
  4. 4. package main import ( "fmt" "math" ) func main() { fmt.Println(math.Pi) } package 中大寫字首的名稱 會自動公開給外部使用
  5. 5. package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 函式宣告 型別都在名稱之後 函式的傳回值型別一 樣是放在函式名稱後
  6. 6. package main import "fmt" func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 同時宣告兩個 int 型別的參數
  7. 7. package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) } 傳回兩個值
  8. 8. package main import "fmt" func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return } func main() { fmt.Println(split(17)) } 具名的結果值參數 空的 return 會傳回具名 結果值參數的目前值
  9. 9. package main import "fmt" var i int var c, python, java bool func main() { fmt.Println(i, c, python, java) } 變數宣告 (型別一樣在名稱後面)
  10. 10. package main import "fmt" var i, j int = 1, 2 var c, python, java = true, false, "no!" func main() { fmt.Println(i, j, c, python, java) } 宣告時可直接設定初始值 有設定初值時可省略型別
  11. 11. package main import "fmt" func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) } 使用 := 可省略 var , 並依照初值自動選用型別
  12. 12. package main import ( "fmt" "math/cmplx" ) var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) func main() { const f = "%T(%v)n" fmt.Printf(f, ToBe, ToBe) fmt.Printf(f, MaxInt, MaxInt) fmt.Printf(f, z, z) } int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64 complex64 complex128
  13. 13. package main import ( "fmt" "math" ) func main() { var x, y int = 3, 4 var f float64 = math.Sqrt(float64(3*3 + 4*4)) var z int = int(f) fmt.Println(x, y, z) } T() 可轉型到 T 不同型別的運算一定要強制轉型
  14. 14. package main import "fmt" const Pi = 3.14 func main() { const World = "世界" fmt.Println("Hello", World) fmt.Println("Happy", Pi, "Day") const Truth = true fmt.Println("Go rules?", Truth) } 宣告常數 常數不能用 := 運算器
  15. 15. package main import "fmt" const ( Big = 1 << 100 Small = Big >> 99 ) func needInt(x int) int { return x*10 + 1 } func needFloat(x float64) float64 { return x * 0.1 } func main() { fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) } 常數會隨語境自動選用適當的型別 如果執行 needInt(Big) 就會發生溢位
  16. 16. package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) } 基本用法如同 C 中的 for 但要注意不用加括號
  17. 17. package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) } package main import "fmt" func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) } 省略迴圈條件判斷前、後的敘述 此時連分號都可以省略
  18. 18. package main func main() { for { } } 什麼都省略
  19. 19. package main import ( "fmt" "math" ) func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) } 條件判斷式不用括號 一定要加大括號
  20. 20. package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } 條件判斷式不用括號 v 只在 if 區塊中有效
  21. 21. package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %gn", v, lim) } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } v 在 else 區塊中也有效
  22. 22. z = 𝑧 − 𝑧2 − 𝑥 2𝑧 package main import ( "fmt" ) func Sqrt(x float64) float64 { z := 1.0 for i := 0;i < 10;i++ { z = z - (z*z - x)/(2*z) } return z } func main() { fmt.Println(Sqrt(2)) }
  23. 23. …… func Sqrt(x float64) (z float64,i int) { z = 1.0 preZ := 0.0 for i = 0 ;math.Abs(z - preZ) > 0.000000001;i++ { preZ = z z = z - (z*z - x)/(2*z) } return } func main() { fmt.Println(Sqrt(2)) } z = 𝑧 − 𝑧2 − 𝑥 2𝑧
  24. 24. package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) } Vertex 是新的型別 每個 Vertex 資料是一個結構體 結構體就是一組 資料欄位的集合 個別欄位使用 . 運算器存取 結構體使用大括號設定初值
  25. 25. package main import "fmt" type Vertex struct { X int Y int } func main() { p := Vertex{1, 2} q := &p q.X = 1e9 fmt.Println(p) } 可宣告指向結 構體的指位器 使用指位器時會自動存取 (Transparent) 所指向的結構 體, 所以沒有 C 的指位器運算
  26. 26. package main import "fmt" type Vertex struct { X, Y int } var ( p = Vertex{1, 2} // 以字面值建立 Vertex q = &Vertex{1, 2} // 宣告指向 Vertex 的指位器 r = Vertex{X: 1} // 只指定 x 初值, Y:0 用預設值 s = Vertex{} // X:0 與 Y:0 都是預設值 ) func main() { fmt.Println(p, q, r, s) } 會顯示 &{1,2}
  27. 27. package main import "fmt" type Vertex struct { X, Y int } func main() { v := new(Vertex) fmt.Println(v) // &{0,0} v.X, v.Y = 11, 9 fmt.Println(v) // &{11,9} } 也可寫成 var v = new(Vertex)
  28. 28. package main import "fmt" func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) } 陣列長度要是常數 不可更改 不可設定初值
  29. 29. package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) fmt.Println("p[1:4] ==", p[1:4]) // 省略起始索引值時預設為 0 fmt.Println("p[:3] ==", p[:3]) // 省略終點索引值時預設為 len(s) fmt.Println("p[4:] ==", p[4:]) } 從 p[1] 到 p[3] p[1:1] -> [] p[1,2] -> [3] 以字面值建立 整數陣列 取得指向後面字面 值陣列的切片
  30. 30. package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) // [0,0,0,0,0], len:5, cap:5 b := make([]int, 0, 5) printSlice("b", b) // [], len:0, cap:5 c := b[:2] printSlice("c", c) // [0,0], len:2, cap:5 d := c[2:5] printSlice("d", d) // [0,0,0],len:3,cap:3 } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %vn", s, len(x), cap(x), x) } 先建立 1 個含有 5 個 0 值的陣列, 傳回指向此陣列所有元素的切片 先建立 1 個含有 5 個 0 值的陣列, 傳回指向此陣列 0 個元素的切片 此 3 個切片都指向同一個陣列 切片起點到陣列最後元素的長度 切片本身 的長度
  31. 31. package main import "fmt" func main() { var z []int fmt.Println(z, len(z), cap(z))// [],0,0 if z == nil { fmt.Println("nil!") } } 沒有元素的切片
  32. 32. package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %dn", i, v) } } i 依序為 0,1,2,3,4… v 依序為 1,2,4,8,16…
  33. 33. package main import "fmt" func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) } for _, value := range pow { fmt.Printf("%dn", value) } } 省略元素變數 只使用索引值 不需要索引變數 時, 要寫 _
  34. 34. package main import "code.google.com/p/go-tour/pic" func Pic(dx, dy int) [][]uint8 { var pic = make([][]uint8,dy) for i := 0;i < dy;i++ { pic[i] = make([]uint8,dx) } for i:=0;i<dy;i++ { for j:=0;j<dx;j++ { pic[i][j] = uint8(i * j); } } return pic } func main() { pic.Show(Pic) }
  35. 35. package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) } 以字串為索引鍵、個別 元素為 Vertex 的 map map 一定要 make 才能用 否則 m 為 nil, 無法設值
  36. 36. …… type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, } func main() { fmt.Println(m) } 索引鍵:元素值 注意元素後要有逗號 用大括號括起來
  37. 37. package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, } func main() { fmt.Println(m) } 個別元素設定時省略 型別名稱
  38. 38. package main import "fmt" func main() { m := make(map[string]int) m["Answer"] = 42 // 新增元素 fmt.Println("The value:", m["Answer"]) m["Answer"] = 48 // 修改元素 fmt.Println("The value:", m["Answer"]) delete(m, "Answer") // 刪除元素 fmt.Println("The value:", m["Answer"]) v, ok := m["Answer"] // 讀取元素, 元素存在時 ok 為 true fmt.Println("The value:", v, "Present?", ok) }
  39. 39. package main import ( "code.google.com/p/go-tour/wc" "strings" ) func WordCount(s string) map[string]int { wc := make(map[string]int) for _,w := range strings.Fields(s) { wc[w] = wc[w] + 1; } return wc } func main() { wc.Test(WordCount) }
  40. 40. package main import ( "fmt" "math" ) func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(3, 4)) } 將定義好的函式設定給變數
  41. 41. package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } 叫用 adder 會把當時的 sum 變數封裝一份到傳回的函式中 func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } } pos 與 neg 各自擁有 一份專屬的 sum 變數
  42. 42. package main import "fmt" func fibonacci() func() int { now, next := 0,1 return func() int { now, next = next, now + next return now } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
  43. 43. package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, plan9, windows... fmt.Printf("%s.", os) } } 前置處理
  44. 44. …… func main() { fmt.Println("When's Saturday?") today := time.Now().Weekday() switch time.Saturday { case today + 0: fmt.Println("Today.") case today + 1: fmt.Println("Tomorrow.") case today + 2: fmt.Println("In two days.") default: fmt.Println("Too far away.") } } 不用 break
  45. 45. package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } } 沒有用來當判斷條件的變數
  46. 46. package main import ( "fmt" "math/cmplx" ) func Cbrt(x complex128) complex128 { z := 1 + 0i for i:= 0;i<10;i++ { z = z - (z * z *z - x)/(3 * z * z) } return z } func main() { fmt.Println(Cbrt(2)) fmt.Println(cmplx.Pow(2, 1.0/3)) // 驗證是否正確? } 記得要用浮點數 z = 𝑧 − 𝑧3 − 𝑥 3𝑧2
  47. 47. package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Println(v.Abs()) } 為 Vertex 定義方法 叫用 Vertex 的方法
  48. 48. …… type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) } 為 Vertex 定義方法 叫用 MyFloat 的方 法
  49. 49. …… type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} v.Scale(5) fmt.Println(v, v.Abs()) // &{15, 20}, 25 } 以指標形式為 Vertex 定義方法 會修改到 v 的內容 v 是指標, 所以 會修改到 v 所 指向的結構體
  50. 50. func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt( v.X*v.X + v.Y*v.Y) } …… type Abser interface { Abs() float64 } func main() { var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // MyFloat 是 Abser a = &v // *Vertex 是 Abser a = v // Vertex 不是 Abser fmt.Println(a.Abs()) } type MyFloat float64 無法成功編譯 相符的方法
  51. 51. package main import ( "fmt" "os" ) type Reader interface { Read(b []byte) ( n int, err error) } type Writer interface { Write(b []byte) ( n int, err error) } type ReadWriter interface { Reader Writer } func main() { var w Writer // os.Stdout implements Writer w = os.Stdout fmt.Fprintf(w, "hello, writern") } 實作介面並不需要任何宣告 實作介面的型別和介面定義所在的 package 不同, 兩者完全不相依賴, 可更正確定義抽象 化的介面
  52. 52. func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } } package main import ( "fmt" "time" ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf( "at %v, %s", e.When, e.What) } 實作了 內建的 Println 遇到 error 介面時 會自動叫用介面中的 Error() 方法 type error interface { Error() string }
  53. 53. func Sqrt(f float64) ( float64, error) { if f >= 0 { z := 1.0 for i := 0;i < 10;i++ { z = z – (z*z - f)/(2*z) } return z, nil } var e ErrNegativeSqrt = ErrNegativeSqrt(f) return 0, e } package main import "fmt" type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf( "無法求 %f 的平方根", e) } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) } 實作 error 介面 傳回 error 介面
  54. 54. http package
  55. 55. package main import ( "fmt" "net/http" ) type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello!") } func main() { var h Hello http.ListenAndServe("localhost:4000", h) } 實作 net/http package中 所定義的 Handler 介面 type Handler interface { ServeHTTP( w ResponseWriter, r *Request) }
  56. 56. func (s *Struct) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s.Greeting+s.Punct+s.Who) } func main() { http.Handle("/string", String("I'm a frayed knot.")) http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"}) http.ListenAndServe( "localhost:4000", nil) } package main import "net/http" import "fmt" type String string type Struct struct { Greeting string Punct string Who string } func (s String) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s) } 實作 Handler 介面 註冊處理路徑 已註冊處理個別路徑 不需再提供 Handler
  57. 57. package main import ( "fmt" "image" ) func main() { m := image.NewRGBA(image.Rect(0, 0, 100, 100)) fmt.Println(m.Bounds()) fmt.Println(m.At(0, 0).RGBA()) } 實作有 image 中所 定義的 Image 介面 type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
  58. 58. package main import ( "code.google.com/p/go-tour/pic" "image" "image/color" ) type Image struct{} func (i Image) ColorModel() color.Model { return color.RGBAModel } func (i Image) Bounds() image.Rectangle { return image.Rect(0,0,200,200) } func (i Image) At(x, y int) color.Color { v := uint8(x * y) return color.RGBA{ v, v, 255, 255 } } func main() { m := Image{} pic.ShowImage(m) } 實作 Image 介面
  59. 59. package main import ( "io" "os" "strings" ) type rot13Reader struct { r io.Reader } func main() { s := strings.NewReader( "Lbh penpxrq gur pbqr!") r := rot13Reader{s} io.Copy(os.Stdout, &r) } func (rot rot13Reader) Read(p []byte)(n int, e error) { n,e = rot.r.Read(p) if n > 0 { for i := 0;i < n;i++ { switch { case p[i] <= 'M': p[i] += 13 case p[i] <= 'Z': p[i] -= 13; case p[i] <= 'm': p[i] += 13 case p[i] <= 'z': p[i] -= 13; } } } return n,e } 實作 Reader 介面
  60. 60. package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { ain(time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func m) { go say("world") say("hello") } 以另一個執行緒執行 say hello world hello world hello world hello world hello 執行結果 (咦?少個 world?)
  61. 61. func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c fmt.Println(x, y, x+y) } package main import "fmt" func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // send sum to c } ① 建立傳遞 int 的通道 ③ 等待通道有空位後 將加總值傳入通道 ④ 等待通道有資料後 一一取出加總值 ② 提供通道 給 gotoutine
  62. 62. package main import "fmt" func main() { c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } 設定通道暫存區大小 等待通道中有資料時讀取等待通道有空位時送入資料
  63. 63. func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } 從通道依序取出資料, 直到 通道清空且傳送端關閉通道 只有傳送端可以關閉通道 若通道清空且傳送端關 閉通道時 ok 為 false v, ok := <-ch
  64. 64. func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) } package main import "fmt" func fibonacci( c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } 專門用在通訊的 switch 多個 case 同時成立 時會隨機挑選執行
  65. 65. …… import "fmt" import "time" func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } } 沒有可用通道時執行 在此區塊嘗試從 tick 或是 boom 通道讀取資料就會進入等待狀態
  66. 66. func Same(t1, t2 *tree.Tree) bool { ch1 := make(chan int) ch2 := make(chan int) go Walk(t1,ch1) go Walk(t2,ch2) for i := 0;i <10;i++ { if <-ch1 != <-ch2 { return false } } return true } func main() { fmt.Println(Same( tree.New(1), tree.New(1))) } package main import ( "code.google.com/p/go- tour/tree" "fmt" ) func Walk(t *tree.Tree, ch chan int) { if t != nil { Walk(t.Left, ch) ch <- t.Value Walk(t.Right, ch) } else { return } }
  67. 67. http://golang.org/ http://golang.org/pkg/ http://golang.org/cmd/ http://golang.org/pkg/fmt/ http://golang.org/pkg/os/
  68. 68. var crawledURLs = map[string]bool {} // 記錄已抓取網頁 func CrawlThis(url string, fetcher Fetcher, foundURLs chan []string) { // 抓取網頁的 goroutine body, urls, err := fetcher.Fetch(url) // 抓取網頁 if err != nil { fmt.Println(err) } else { fmt.Printf("found: %s %qn", url, body) } crawledURLs[url] = true; // 登記此網頁已抓取 foundURLs <- urls // 將此網頁上的連結陣列傳入通道 }
  69. 69. func Crawl(url string, depth int, fetcher Fetcher) { foundURLs := make(chan []string) // 傳遞網頁內連結陣列的通道 go CrawlThis(url, fetcher, foundURLs) // 啟動 goroutine Crawlers := 1 // 目前啟動了 1 個 goroutine for i := 1;i < depth;i++ { // 依據 depth 控制抓取層數 n := Crawlers // 依 goroutine 數從通道取出傳回的連結陣列 for Crawlers = 0;n > 0;n-- { // 將 goroutine 數歸零 urls := <- foundURLs //從通道取出傳回的連結陣列 for _, u := range urls { // 從陣列中一一取出連結 if _, crawled := crawledURLs[u]; !crawled { go CrawlThis(u, fetcher, foundURLs) Crawlers++ } } } } return } 如果此連結尚未抓取過 啟動 coroutine 抓取網頁 遞增 goroutine 數

×