// Description: Golang語法與代碼格式速記 // Author: cxy // Date: 2013-04-01 // Version: 0.3 // TODO 說明 // TODO package // Go是采用語法解析器自動在每行末尾增加分號,所以在寫代碼的時候可以把分號省略。 // Go編程中只有幾個地方需要手工增加分號,如: for循環使用分號把初始化,條件和遍歷元素分開。在一行中有多條語句時,需要增加分號。 // 不能把控制語句(if, for, switch, or select)、函數、方法 的左大括號單獨放在一行, 如果你這樣作了語法解析器會在大括號之前插入一個分號,導致編譯錯誤。 // 引用包名與導入路徑的最后一個目錄一致 import "fmt" import "math/rand" fmt.Println(rand.Intn(10)) // 0到10之間的非負偽隨機數 // 用圓括號組合導入包,這是“factored”導入語句 import ("fmt"; "math") import ( "fmt" "math" ) // 導入包可以定義別名,防止同名稱的包沖突 import n "net/http" import ( 控制台 "fmt" m "math" ) 控制台.Println(m.Pi) // 首字母大寫的名稱是被導出的, 首字母小寫的名稱只能在同一包內訪問(同包跨文件也能訪問) var In int // In is public var in byte // in is private var 看不見 string // 看不見 is private const Com bool = false // Com is public const 還是看不見 uint8 = 1 // 還是看不見 is private type Integer int // Integer is public type ブーリアン *bool // ブーリアン is private func Export() {} // Export is public func 導入() {} // 導入 is private func (me *Integer) valueOf(s string) int {} // valueOf is private func (i ブーリアン) String() string {} // String is public // Go 的基本類型: ┌──────┬─────────┬────────┬─────────┬───────────┬────────────┐ │ bool │ string │ │ │ │ │ ├──────┼─────────┼────────┼─────────┼───────────┼────────────┤ │ int │ int8 │ int16 │ int32 │ int64 │ │ │ │ │ │ rune │ │ │ ├──────┼─────────┼────────┼─────────┼───────────┼────────────┤ │ uint │ uint8 │ uint16 │ uint32 │ uint64 │ uintptr │ │ │ byte │ │ │ │ │ ├──────┼─────────┼────────┼─────────┼───────────┼────────────┤ │ │ │ │ float32 │ float64 │ │ ├──────┼─────────┼────────┼─────────┼───────────┼────────────┤ │ │ │ │ │ complex64 │ complex128 │ └──────┴─────────┴────────┴─────────┴───────────┴────────────┘ // byte 是 uint8 的別名 // rune 是 int32 的別名,代表一個Unicode碼點 // 變量聲明, 使用var關鍵字 (Go中只能使用var聲明變量,無需顯式初始化值) var i int // i = 0 var s string // s = "" (Go中的string不存在nil(null)值,默認零值就是空串 "" 或 ``) var e error // e = nil, error是Go的內建接口類型,不是基本類型。 // var 語句聲明了一個變量的列表,類型在變量名之后 var a,b,c int // a = 0, b = 0, c = 0 var ( a int // a = 0 b string // b = "" c uint // c = 0 ) // 變量定義時初始化賦值,每個變量對應一個值 var a int = 0 var a,b int = 0, 1 // 初始化使用表達式時,可以省略類型,變量從初始值中獲得類型 var a = 'A' // a int32 c := 1 + 2i // c complex128 var a,b = 0, "B" // a int, b string a, b := 0, "B" // a int, b string c := `formatted string` // c string // := 結構不能使用在函數外,函數外的每個語法塊都必須以關鍵字開始 // 常量可以是字符、字符串、布爾或數字類型的值,數值常量是高精度的值 const x int = 3 const ( a byte = 'A' b string = "B" c bool = true d int = 4 e float32 = 5.1 f complex64 = 6 + 6i ) // 未指定類型的常量由常量值決定其類型 const a = 0 // a int const ( b = 2.3 // b float64 c = true // c bool ) // 自動枚舉常量 iota // iota的枚舉值可以賦值給數值兼容類型 // 每個常量單獨聲明時, iota不會自動遞增(無意義) const a int = iota // a = 0 const b int = iota // b = 0 const c byte = iota // c = 0 const d uint64 = iota // d = 0 // 常量組合聲明時, iota每次引用會逐步自增, 初始值為0,步進值為1 const ( a uint8 = iota // a = 0 b int16 = iota // b = 1 c rune = iota // c = 2 d float64 = iota // d = 3 e uintptr = iota // e = 4 ) // 枚舉的常量都為同一類型時, 可以使用簡單序列格式. const ( a = iota // a int32 = 0 b // b int32 = 1 c // c int32 = 2 ) // 枚舉序列中的未指定類型的常量會跟隨序列前面最后一次出現類型定義的類型 const ( a byte = iota // a uint8 = 0 b // b uint8 = 1 c // c uint8 = 2 d rune = iota // d int32 = 3 e // e int32 = 4 f // f int32 = 5 ) // iota自增值只在一個常量定義組合中有效,跳出常量組合定義后iota值歸0 const ( a = iota // a int32 = 0 b // b int32 = 1 c // c int32 = 2 ) const ( e = iota // e int32 = 0 (iota重新初始化並自增) f // f int32 = 1 ) // 定制iota序列初始值與步進值 (通過數學公式實現) const ( a = (iota + 2) * 3 // a int32 = 0 (a=(0+2)*3) 初始值為6,步進值為3 b // b int32 = 3 (b=(1+2)*3) c // c int32 = 6 (c=(2+2)*3) d // d int32 = 9 (d=(3+2)*3) ) // 數組聲明帶有長度信息,數組的長度固定 var a [3]int = [3]int{0, 1, 2} // a = [0 1 2] var b [3]int = [3]int{} // b = [0 0 0] var c = [3]int{} // c = [0 0 0] d := [3]int{} // d = [0 0 0] fmt.Printf("%T\t%#v\t%d\t%d\n", d, d, len(d), cap(d)) // [3]int [3]int{0, 0, 0} 3 3 // 使用自動計算數組初始數據的長度 var a = []int{0, 1, 2} x := [][3]int{{0, 1, 2}, {3, 4, 5}} // slice 指向數組的值,並且同時包含了長度信息 var a []int fmt.Printf("%T\t%#v\t%d\t%d\n", a, a, len(a), cap(a)) // []int []int(nil) 0 0 var a = new([]int) fmt.Printf("%T\t%#v\t%d\t%d\n", a, a, len(*a), cap(*a)) // *[]int &[]int(nil) 0 0 var b = make([]int, 0) fmt.Printf("%T\t%#v\t%d\t%d\n", b, b, len(b), cap(b)) // []int []int{} 0 0 var c = make([]int, 3, 10) fmt.Printf("%T\t%#v\t%d\t%d\n", c, c, len(c), cap(c)) // []int []int{} 3 10 var d []int = []int{0, 1, 2} fmt.Printf("%T\t%#v\t%d\t%d\n", d, d, len(d), cap(d)) // []int []int{0, 1, 2} 3 3 // slice 可以重新切片,創建一個新的 slice 值指向相同的數組 s := []int{0, 1, 2, 3, 4} fmt.Println(s[1,3]) // [1 2] (截取從開始索引到結束索引-1 之間的片段) fmt.Println(s[:4]) // [0 1 2 3] fmt.Println(s[1:]) // [1 2 3 4] fmt.Println(s[1:1]) // [] // 向slice中添加元素 s := make([]string, 3) s = append(s, "a") // map 在使用之前必須用 make 來創建(不是 new);一個值為 nil 的 map 是空的,並且不能賦值 var m map[int]int m[0] = 0 // × runtime error: assignment to entry in nil map fmt.Printf("type: %T\n", m) // map[int]int fmt.Printf("value: %#v\n", m) // map[int]int(nil) fmt.Printf("value: %v\n", m) // map[] fmt.Println("is nil: ", nil == m) // true fmt.Println("length: ", len(m)) // 0,if m is nil, len(m) is zero. var m map[int]int = make(map[int]int) m[0] = 0 // 插入或修改元素 fmt.Printf("type: %T\n", m) // map[int]int fmt.Printf("value: %#v\n", m) // map[int]int(0:0) fmt.Printf("value: %v\n", m) // map[0:0] fmt.Println("is nil: ", nil == m) // false fmt.Println("length: ", len(m)) // 1 m = map[int]int{ 0:0, 1:1, // 最后的逗號是必須的 } m = map[string]S{ "a":S{0,1}, "b":{2,3}, // 類型名稱可省略 } a := m["a"] // 取值 a, ok := m["a"] // 取值, 並通過ok(bool)判斷key對應的元素是否存在. delete(m, "a") // 刪除key對應的元素. // 結構體(struct)就是一個字段的集合, type 定義跟其字面意思相符 type S struct { A int B, c string } type ( A struct { s *S } B struct { A // 組合 } ) // 結構體文法表示通過結構體字段的值作為列表來新分配一個結構體。 var s S = S{0, "1", "2"} // 使用 Name: 語法可以僅列出部分字段。(字段名的順序無關。) var s S = S{A: 0, B: "1"} var s S = S{} // 特殊的前綴 & 構造了指向結構體文法的指針。 var s *S = &S{0, "1", "2"} // 表達式 new(T) 分配了一個零初始化的 T 值,並返回指向它的指針 var s *S = new(S) // 有指針,但是沒有指針運算,結構體字段使用點號來訪問 // 結構體字段可以通過結構體指針來訪問。通過指針間接的訪問是透明的 fmt.Println(s.A) fmt.Println((*s).A) // TODO interface type IF interface { a() } // TODO chanel // TODO error // if 語句 小括號 ( )是可選的,而 { } 是必須的。 if (i < 0) // 編譯錯誤. println(i) if i < 0 // 編譯錯誤. println(i) if (i < 0) { // 編譯通過. println(i) } if i < 0 { println(i) } else { println(i) } // 可以在條件之前執行一個簡單的語句,由這個語句定義的變量的作用域僅在 if/else 范圍之內 if (i := 0; i < 1) { // 編譯錯誤. println(i) } if i := 0; (i < 1) { // 編譯通過. println(i) } if i := 0; i < 0 { // 使用gofmt格式化代碼會自動移除代碼中不必要的小括號( ) println(i) } else if i == 0 { println(i) } else { println(i) } // if語句作用域范圍內定義的變量會覆蓋外部同名變量,(與方法函數內局部變量覆蓋全局變量相同) a, b := 0, 1 if a, b := 3, 4; a > 1 && b > 2 { println(a, b) // 3 4 } println(a, b) // 0 1 // 只有一種循環結構,for 循環。可以讓前置、后置語句為空,或者全為空 for i := 0; i < 10; i++ {} for i := 0; i < 10; {} for ; i < 10; i++ {} for ; i < 10; {} for i < 10 {} for ; ; {} for {} // 小括號 ( )是可選的,而 { } 是必須的。 for (i := 0; i < 10; i++) {} // 編譯錯誤. for i := 0; (i < 10); i++ {} // 編譯通過. for (i < 10) {} // 編譯通過. // TODO continue // TODO for range // TODO switch // TODO fallthrough break // TODO type assertion // TODO select // TODO goto // 函數可以沒有參數或接受多個參數 func f() {} func f(a int) {} func f(a int, b byte) {} func f(a int) {} // 可變參數 func f(a int, b bool, c string) {} // 函數可以返回任意數量的返回值 func f() int { return 0 } func f() int, string { return 0, "A" } // 函數返回結果參數,可以像變量那樣命名和使用 func f() a int, b string { a = 1 b = "B" return // 或者 return a, b } // 當兩個或多個連續的函數命名參數是同一類型,則除了最后一個類型之外,其他都可以省略 func f(a,b,c int) {} func f() a,b,c int {} func f(a,b,c int) x,y,z int {} // 函數也是值,可以將函數賦值給變量 var f (func(i int) int) = func(i int) int { return i } fmt.Println(f(3)) // 3 var f func() int = func() int { return 0 } fmt.Println(f()) // 0 var f func() = func() {} var f = func() {} f := func() {} // TODO defer // TODO 方法 // TODO 內建函數 append cap close complex copy delete imag len make new panic print println real recover // TODO 並發 go func() {}