【学习笔记】《Go 指南》
學習筆記 —— Go 指南
前言:先貼上網址,因為是先用 Typora 寫,然后直接導入的,所以格式多多少少有點問題= =
文章目錄
- 學習筆記 —— Go 指南
- 一. 包、變量和函數
- 二. 流程控制語句 for、switch、if 和 defer
- 三. 更多類型 struct、slice 和映射
- 四. 方法和接口
- 五. 并發
一. 包、變量和函數
導包可以用分組形式(括號)
import ("fmt""math" )在 Go 中,如果一個名字以大寫字母開頭,那么它就是已導出的。
math.pi // 未導出的變量無法訪問 math.Pi函數參數類型在后;連續同類型時可以省略,只在最后一個參數寫上類型
func add(x, y int) int {return x + y }多值返回:函數可以返回任意數量的返回值。
func swap(x, y string) string {return y, x }返回值可被命名,位于函數頂部。
無參的 return 語句會直接返回已命名的返回值。(長函數這樣使用會影響可讀性)
// 已命名返回值 x, y func split(sum int) (x, y int) {x = sum % 10;y = sum / 10;// 會返回 x, yreturn }var 語句:用于聲明一個變量列表,跟函數的參數列表一樣,類型在最后。
var 語句可以出現在包或函數級別。
package mainimport "fmt"var java, python, golang bool func main(){var i intfmt.Println(i, java, python, golang) }聲明可以包含初始化值
有初始化值時,可以省略類型,會從初始化值中自動獲取。
var i, j int = 1, 3 // 自動獲取;可以類型不同! var golang, java = true, "java~"短變量聲明:在函數中,可以用 “:=” 來代替 var 聲明語句。(函數外不行,因為函數外要求語句以關鍵字開始)
func main(){k := 3i, golang, java := 3, true, "java~" }基本類型: 居然沒有字符= =
boolstring// int, uint 和 uintptr 在 32 位系統上通常為 32 位寬,在 64 位系統上則為 64 位寬。 // 整形應使用 int 類型,除非特需使用固定大小 or 無符號的整數類型。 int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr// uint8 的別名 byte // int32 的別名, 表示一個 Unicode 碼點 rune // 兩種大小的 float 型 float32 float64// 兩種大小的 復數 complex64 complex128 // var 也可以像 import 一樣分組~ var (myBool bool = truemyString string = "fwy" )默認值
// 數值默認 0 var i int var j float32 // 布爾默認 false var is bool// 字符串默認 ""(空串) var name string類型轉換:只能顯式轉換
// 注意:go 不支持隱式轉換,設計者認為這個功能的弊端比好處多。 var i, j = 1, 2 var k float32 = float32(i + j)// 短聲明 i, j := 1, 2 k := float32(i + j)類型推導:變量類型由右值推導
var i int = 3 // 推導 j is type of int var j = i // 當右邊包含未指明類型的數值常量時,新變量的類型取決于常量的精度: i := 3 // int j := 3.14 // float64 k := 3 + 3.14 // float64常量: const 關鍵字。不能用 := 聲明
// 常量可以是字符串、布爾值或數值 const world = "世界"// 可以使用分組、可以指定類型 const (name int = 20school = "SZU" )數值常量:數值常量是高精度的 值。一個未指定類型的常量由上下文來決定其類型。
const (Big = 1 << 100Small = Big >> 99 )func needInt(x int) int {return x }func needFloat(x float32) float32 {return x }func main(){// 兩個都可以跑,因為常量類型未指定fmt.Println(needInt(Small))fmt.Println(needFloat(Small))fmt.Println(needFloat(Big))fmt.Println(needInt(Big)) // error: overflows int }二. 流程控制語句 for、switch、if 和 defer
循環語句:
sum := 0 /*** 1. go 只有一種循環:for 循環* 2. 三個構成部分外沒有小括號,用 ';' 隔開* 3. 大括號{ }是必須的 */ for i := 0; i < 10; i++ {sum += i }// 4. 初始化 & 后置語句可省略 for ; sum < 100; {sum += sum }// 5. for 是 go 中的 "while"。去掉分號,有: for sum < 1000 {sum += sum }// 6. 條件也可省略。下例為“緊湊的無限循環” for { }判斷語句:
// 1. 和 for 一樣:省略小括號,必須大括號 you := true if you {fmt.Println("you~you~you") }// 2. 和 for 一樣:在條件表達式之前,可以執行一個簡單語句 if me := 3 * 4; me == 12 {fmt.Prinln("簡單語句聲明的變量,作用域僅在該if語句中") } // 當然,對應的 else 塊中也可以使用該變量 else {me += me }switch 語句:
/*** 1. Go 自動提供了在這些語言中每個 case 后面所需的 break 語句。* 2. 除非以 fallthrough 語句結束,否則分支會自動終止 (接1)* 3. case 無需為常量,且取值不必為整數* 4. 同 if ,可以在先執行一個簡單語句 */ func main(){switch os := "macOS"; os {case "Linux":fmt.Println("LinuxOS")case "mac":fmt.Println("macOS")default:fmt.Println(os)}// 5. switch == switch true (無條件 switch 相當于 switch true) }defer 語句:將函數推遲到外層函數返回之后執行。
參數會立即求值,但是函數要返回之后才調用。
func main(){defer fmt.Println("hello")fmt.Print("world")// 輸出:world hello }defer 棧:推遲的函數調用會被壓入一個棧中。當外層函數返回時,被推遲的函數會按照后進先出的順序調用。
func main(){for i := 1; i < 4; i++ {defer fmt.Print(i)}fmt.Print("end")// 輸出:end 3 2 1 }三. 更多類型 struct、slice 和映射
指針:
// 和 C++ 相同的兩個運算符:*、& var(a int = 3// 注意定義時 * 在類型前p *int = &a ) fmt.Println(*p) // 零值為 nil p = nil結構體:一個結構體(struct)就是一組字段(field)
// 聲明格式 type Vertex struct {x inty int } func main(){// 1. 大括號構造temp := Vertex{1, 2}// 2. 用'.'來訪問成員fmt.Println(temp.x)// 3. 結構體指針p := &temp// 4. 允許隱式間接引用fmt.Println((*p).x) // 這樣寫太啰嗦了fmt.Println(p.x)// 5. 使用 Name: 語法可以僅列出部分字段。(順序無關)temp := Vertex{x: 1} // 1, 0temp := Vertex{} // 0, 0 }數組:
// var 格式 var a [2]int a[0] = 3// := 格式 stinrgs := [2]string{"fwy", "szu"}切片1:動態、靈活,在實踐中比數組更常用
arr1 := [3]int{1, 2, 3} // 1. [low, height) 閉開區間 // 不存儲數據,只是描述數組的一部分(共享同一個數組) sec1 := arr1[1 : 2]// 2. 創建一個數組,然后構建一個引用了它的切片(此處[]內沒有大小聲明) sec2 := []int{1, 2, 3}// 3. 以默認忽略上下界(下界為0,上界為切片長度) arr2 := [3]int{1, 2, 3} // 這四個切片實際上相同 sec3 := arr[: 3] sec4 := arr[0 :] sec5 := arr[:] sec6 := arr[0 : 3]切片2:
// capacity:從切片第一個元素,到其底層數組元素末尾的個數 // length:包含的元素個數 s := []int{1, 2, 3, 4, 5} // 5, 5 fmt.Println(cap(s), len(s)) // 直接通過cap()、len() 獲取// 零值 nil:len 和 cap 都是0,而且【沒有底層數組】 var ss []int切片3:make 函數:分配一個元素為零值的數組,并返回一個引用了它的切片
? 這也是分配動態數組的方式
a := make([]int, 5) // 指定容量,則加入第三個參數 b := make([]int, 5, 7)// 切片可包含任何類型,包括其它的切片。 board := [][]string{[]string{"-", "-", "-"},[]string{"-", "-", "-"}, }切片4:append 追加元素
// 1. 追加到末尾 // 2. 容量不夠時,會創造新的切片,然后再返回新切片 var s []int append(s, 0) // 3. 可一次添加多個元素 append(s, 1, 2, 3)for 循環的 range 形式
// 1. 遍歷切片時,每次會返回兩個值:index、element的副本 var arr = []int{1, 2, 3} for i, v := range pow {fmt.Prinln(i, v) }// 2. 可以用 _ 來忽略返回值 for i, _ := range pow for _, v := range pow // 3. 如果只需要索引,也可以直接忽略第二個參數 for i := range pow映射1:
// 1. 零值為 nil:既沒有鍵,也不能添加鍵 // 2. make 函數會返回給定類型的映射,并將其初始化備用 m := make(map[string]int) m["fwy's age"] = 20 // 3. 必須有鍵名 var mm = map[string]int {"fwy" : 20"JOJO" : 200 } // 4. 若頂級類型只是一個類型名,你可以在文法的元素中省略它。 var mmm = map[string]Vertex{"Bell Labs": Vertex{40.68433, -74.39967,},// 省略"Google": {37.42202, -122.08408,}, }映射2:
// 增 or 改 m["fwy"] = 21 // 查 age = m["fwy"] // 刪 delete(m, key) // contains:通過雙賦值實現 element, ok = m[key] // 存在則 ok == true,否則 ok == false // 不存在則 element 為類型 nil 值 // 如果 element, ok 未聲明,可以用短變量聲明 element, ok := m[key]函數
// 函數也是值。它們可以像其它值一樣傳遞。(有點離譜。。但是也是這么個理) func compute(fn func(float64, float64) float64) float64{return fn(3, 4) } // 翻譯:compute函數的參數為“一個返回值為float型的,需要兩個float參數的函數”,返回值為float類型func main(){// 這個也挺離譜的,整得有點像 Java 的匿名類了= =mySqrt := func(x, y float64) {return math.Sqrt(x * x, y * y)}compute(mySqrt()) }函數的閉包
// 閉包是一個函數值,它引用了其函數體之外的變量。該函數可以訪問并賦予其引用的變量的值 // adder() 的返回值類型 func(int) int func adder() func(int) int {sum := 0return func(x int) int {sum += xreturn sum} }func main() {pos, neg := adder(), adder()fmt.Println(pos(1)) // 1fmt.Println(neg(-1)) // -1 } // 斐波那契閉包 func fibonacci() func() int {// 初始化,這行代碼只跑了一次a, b := 0, 1// func() 一直引用了函數體外的a、b,并且進行了修改return func() int {temp := aa, b = b, a + breturn temp}func main() {fibo := fibonacci()for i := 0; i < 10; i++ {fmt.Println(fibo())}} }四. 方法和接口
方法:
// 1. go 沒有類,但是可以給結構體定義“方法” // 2. 方法: 帶特殊的“接收者參數”的函數(方法即函數) type Vertex struct {x inty int }// 3. 接收者參數,位于函數名前 func (v Vertex) getX() int {return v.x }// 4. 只能為包內定義的類型的接收者聲明方法(包外,像int 之類的內建類型也不行) // 可以通過這種方法曲線救國 type myInt int func (v myInt) getInt() int {return (int)v }// 5. 為指針接收者聲明方法 func (v *Vertex) scale(num int) {v.x *= numv.y *= num }// 如果不是指針,而是值,那么將會在副本上修改,而不是本體(對于函數的其他參數也是如此) func (v Vertex) scale(num int) {v.x *= numv.y *= num }// 6. 帶指針參數的函數必須接受一個指針,而以指針為接收者的函數則“值和指針都可以” // 相反,值也是一樣 p.Abs() // 會被解釋為 (*p).Abs()// 7. 使用指針為接收者好處: // 1)可以修改值 // 2)不用復制值,造成資源浪費接口 def
// 接口類型 是由一組方法簽名定義的集合。 // 接口類型的變量可以保存任何實現了這些方法的值。 type Abser interface {Abs() float64 }type MyFloat float64 func (mf MyFloat) Abs() float64 {return float64(-mf); }type Vertex struct {x, y float64 }func (v *Vertex) Abs() float64 {return x + y }func main() {var (abser Absermf = math.Sqrt(30)v = Vertex{3, 4})abser = MyFloat(mf)abser.Abs()abser = &vabser.Abs()// error: abser = v } // 1. 沒有 implements 關鍵字,無需顯示聲明// 2. 接口也是值,可以傳遞。 // 可以看做包含值和具體類型的元組:(value, type),會保存一個具體底層類型的具體值// 3. 即便接口內的具體值為 nil,方法仍然會被 nil 接收者調用。“底層值為 nil 的接口值” // ps: 保存了 nil 具體值的接口其自身并不為 nil。// 4. nil 接口值:和 3 不同,會產生運行時錯誤。“nil 接口值” // 也就是找不到具體方法的值,而 3 則是找到具體方法,找不到具體對象 type MyInterface interface {M() } func main() {var i MyInterfacei.M() }空接口:定義了 0 個方法的接口,可保存任何類型的值
// 被用來處理未知類型的值 func main() {var i interface{}fmt.Printf("(%v, %T)\n", i, i)// (<nil>, <nil>)i = 42fmt.Printf("(%v, %T)\n", i, i)// (42, int)i = "hello"fmt.Printf("(%v, %T)\n", i, i)// (hello, string) }**類型斷言:**提供了訪問接口值底層具體值的方式。
// t = i.(T) func main() {var i interface{} = "hello"s := i.(string) // hellos, ok := i.(string) // hello yes// 將會賦予對應零值f, ok := i.(float64) // 0, false// 失敗,且沒有接收判斷值的情況會報錯f := i.(float64) }類型選擇
func do(i interface{}) {switch v := i.(type) {case int : fmt.Println("int")case string:fmt.Println("string") default:fmt.Println("I don't know how %T !", v)} }func main() {do(32)do("hello")do(true) }Stringer:屬于 fmt 包
type Stringer interface {// 用于描述自己,類似 Java 的 toString()String() string }錯誤:類似5,是一個內建接口
type error interface {Error() string }// 通常函數會返回一個 error 值, error 值為 nil 時成功,否則失敗 type MyError struct {when time.Timewhat string }func (myError *MyError) Error() string {return fmt.Sprintf("%v, %s", myError.when, myError.what) }func run() error {return &MyError{time.Now(),"it didn't work" } }func main() {if err := run(); err != nil {fmt.Println(err)} }Reader: io 包
func main() {// 新建一個 Readerr := strings.NewReader("Hello Golang~");// 新建一個容量為8的切片mySlice := make([]byte, 8);for {// func (T) Read(b []byte) (n int, err error)n, err := r.Read(mySlice)fmt.Printf("n = %v err = %s mySlice = %v \n", n, err, mySlice)// 遇到數據流結尾時,會返回一個 EOF 錯誤if err == io.EOF {break;}} }待補充:圖像
五. 并發
Go 程(goroutine): Go 運行時管理的輕量級線程。
// f, x, y 和 z 的求值發生在當前的 Go 程中,而 f 的執行發生在新的 Go 程中。 // go f(x, y, z)func sum(s []int, c chan int) {sum := 0;for _, v := range s {sum += v}// 把 sum 的值送入流信道中c <- sum }func main() {arr := []int{1, 2, 3, -1, -2 ,7}// 創建管道c := make(chan int)// 各負責一半go sum(arr[: len(arr)/2], c)go sum(arr[len(arr)/2 :], c)// 都結束后,再從信道中獲取x, y := <- c, <- cfmt.Println(x + y) }帶緩沖的信道
// 1. 通過在 make() 第二個參數設置緩沖 ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 // 填滿后會阻塞range & close
// 1. 可以通過分配第二個參數來判斷信道是否關閉 ch = make(chan int) // 如果已經關閉了, ok = false v, ok := <-c// 2. 如果使用 range,會不斷從信道接收值,直到他被關閉 for i := range c { }select 語句
// 1. 使一個 Go 程可以等待多個通信操作。 // 2. 會阻塞到任一分支可行,如多個可行則隨機一個 func fibonacci(c, quit chan int) {x, y := 0, 1for {select {case c <- x:x, y = y, x + y// quit 可流出時case <- quit:fmt.Println("quit")returndefault:fmt.Println("其他分支都沒有準備好~")}} }func main() {c := make(chan int)quit := make(chan int)// 有 Java 匿名類內味了go func() {for i := 0; i < 10; i++ {fmt.Println(<- c)}quit <- 1}()fibonacci(c, quit) }總結
以上是生活随笔為你收集整理的【学习笔记】《Go 指南》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python列表添加元素到中间_pyth
- 下一篇: php 网站计数器,PHP实现网站访问量