Go基礎語法筆記(來源 A tour of Go)

簡單例子

package main //當前所在的文件名
import (     //引進包
	"fmt"
	"time"
)
//聲明函數 函數名字 傳入變量以及類型 結果的類型(有幾個就寫幾個)
func add(x, y int) (int) {
	return x + y
} 

func main(){
	fmt.Println("Welcom to Go!",time.Now())
	fmt.Println(add(42,13))
}

包,變量,函數

  • 包:“fmt”,“time”
  • 變量:
  • 聲明方式:
var x float32 =42 
x := float32(42)
  • 常見數據型:
    • bool,
    • string
    • int int8 int16 int32 int64
    • uint uint8 uint16 uint32 uint64 uintptr
    • float32/float64
    • complex64/complex128
    • 函數
    //函數寫法1:聲明 傳入變量 函數名字 結果類型
    func (v Vertex) Abs() float64 {
    	return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    //函數寫法2:聲明 函數名字 傳入變量 結果(有幾個就寫幾個類型)
    func add(x, y int) (int) {
    return x + y
    }
    
    

流程控制語句

  • for
//go的for語句沒有括號
for i := 0; i < 10; i++ {
sum += i
}
//類似於其他語言的while寫法
for sum < 1000 {
sum += sum
}
//無限循環
for {}
- if else
- 
//簡單的判斷
if x<0 {}
//可在裏面正常生成一個
if v := math.Pow(x, n); v < lim {
return v
}
  • switch
switch i {
case 0:xxx,
case f()
}
  • defer
//defer定義的函數會立即求值,但要到外層結束後纔會被調用
//其實質是壓入一個棧
defer fmt.Println("world")

更多類型:指針,struct,數組,slice和映射

  • 指針,指針的零值爲nil
//定義p的值
var p *int
//&操作符生成一個指向其操作數的指針
i := 42
p = &i
//*操作符表示指針指向的底層值
fmt.Println(*p) //通過指針p讀取i
*p = 21         //通過指針p設置成i

package main

import "fmt"

func main() {
i, j := 42, 2701

p := &i         // 指向 i
fmt.Println(*p) // 通過指針讀取 i 的值
*p = 21         // 通過指針設置 i 的值
fmt.Println(i)  // 查看 i 的值

p = &j         // 指向 j
*p = *p / 37   // 通過指針對 j 進行除法運算
fmt.Println(j) // 查看 j 的值
}
  • struct
//define the struct
type Vertex struct {
X int
Y int
}

//usage
v := Vertex{1, 2}
//結構體文法,通過直接列出字段的值來新分配一個結構體
//使用Name:語法可以僅列出部分字段
//特殊前綴&返回一個指向結構體的指針
var (
v1 = Vertex{1, 2}  // 創建一個 Vertex 類型的結構體
v2 = Vertex{X: 1}  // Y:0 被隱式地賦予
v3 = Vertex{}      // X:0 Y:0
p  = &Vertex{1, 2} // 創建一個 *Vertex 類型的結構體(指針)
)
func main() {
fmt.Println(v1, p, v2, v3)
}
  • slice,make
  • 數組和切片的區別:數組一般是定長的,數組切片可以構建成slice
  • slice切片
//define array
var a [10]int
var a [10]int={2,3,5,7,11,13} //a :=[10]int{2,3,5,7,11,13}
//slice the array
a[low:high]
//slice length and capacity
// 截取切片使其長度爲 0
s = s[:0]
printSlice(s)

// 拓展其長度
s = s[:4]
printSlice(s)

// 捨棄前兩個值
s = s[2:]
printSlice(s)
  • make切片
//切片的cap是總容量,len是裏面存在的具體個數
a := make([]int, 5)  // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
  • 切片定義任何類型
// 創建一個井字板(經典遊戲)
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}

// 兩個玩家輪流打上 X 和 O
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
  • 切片擴展
// 添加一個空切片
s = append(s, 0)
printSlice(s)

// 這個切片會按需增長
s = append(s, 1)
printSlice(s)

// 可以一次性添加多個元素
s = append(s, 2, 3, 4)
printSlice(s)
  • range
for i, _ := range pow{}
for _, value := range pow{}
for i := range pow{}
  • 映射 有點類似於js的json
//define the map
var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}
//插入或者修改
m[key] = elem
//獲取元素
elem = m[key]
//刪除元素
delete(m,key)
//通過雙賦值檢查某個鍵是否存在
value,ok=m[key]

方法

  • golang的方法可以作爲值進行傳遞
  • 方法只是個帶接收者參數的函數
//寫法1
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
//寫法2
func Abs(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
  • 和C語言類似的指針調用方法
    *p定義爲指針函數,*p指向的地址纔是實際的地址,使用&p傳入形參
  • 指針爲接受者的方法調用時,接收者即可以爲值也可以爲指針
var v Vertex
v.Scale(5)  // OK
Scale(5) //錯誤
p := &v
p.Scale(10) // OK
Scale(p)//Ok
  • 同樣發生在傳入賦值時
var v Vertex
fmt.Println(v.Abs()) // OK
fmt.Println(Abs(&v)) // 編譯錯誤!
p := &v
fmt.Println(p.Abs()) // OK 這種狀態下被自動解釋爲(*p).Abs()
  • 選擇指針作爲接收者的規律
    • 方法能夠修改其接收者指向的值
    • 這樣可以避免在每次調用方法時複製該值。若值的類型爲大型結構體時,這樣做會更加高效。

接口:一組方法定義簽名定義的組合

  • 定義和使用:

    //例子,interface裏面的方法
    type I interface {
    	M()
    }
    
  • 隱式實現

    package main
    
    import "fmt"
    
    type I interface {
    	M()
    }
    
    type T struct {
    	S string
    }
    
    // 此方法表示類型 T 實現了接口 I,但我們無需顯式聲明此事。
    func (t T) M() {
    	fmt.Println(t.S)
    }
    
    func main() {
    	var i I = T{"hello"}
    	i.M()
    }
    
  • 接口值:接口可作爲值返回,使用標準符號 %T

  • nil值:未定義的接口值默認爲nil,但不從邏輯上排除會報錯

  • 空接口

    var i interface{}
    
  • 類型斷言和類型選擇

    //類型斷言
    t,ok :=i,(Type)
    //example
    t,ok := i.(int)
    //類型選擇
    switch v := i.(type) {
    case T:
        // v 的類型爲 T
    case S:
        // v 的類型爲 S
    default:
        // 沒有匹配,v 與 i 的類型相同
    }
    
  • Stringer:fmt包中函數,可以重寫fmt.Println()函數

    //interface
    type Stringer interface {
        String() string
    }
    //usage
    type Person struct {
        Name string
    	Age  int
    }
    func (p Person) String() string {
    	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
    }
    
    func main() {
    	a := Person{"Arthur Dent", 42}
    	z := Person{"Zaphod Beeblebrox", 9001}
    	fmt.Println(a, z)
    }
    
  • 錯誤:與Stringer相似可以處理相應的值

    func (e *MyError) Error() string {
    	return fmt.Sprintf("at %v, %s",
    		e.When, e.What)
    }
    
  • Reader

    //使用例子
    func main() {
    	r := strings.NewReader("Hello, Reader!")
    
        //切片創造空間
    	b := make([]byte, 8)
    	for {
    		n, err := r.Read(b)
    		fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
    		fmt.Printf("b[:n] = %q\n", b[:n])
    		if err == io.EOF {
    			break
    		}
    	}
    }
    
  • 圖像

    //底層定義
    type Image interface {
        ColorModel() color.Model
        Bounds() Rectangle
        At(x, y int) color.Color
    }
    //使用例子
    func main() {
    	m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    	fmt.Println(m.Bounds())
    	fmt.Println(m.At(0, 0).RGBA())
    }
    
    
    

併發

  • goroutine go程:Go 運行時管理的輕量級線程

    //command
    go f(x,y,z)
    
  • channel 信道:帶有類型的管道,類似於攜程

    //創建,信道在使用前必須創建
    ch := make(chan int);//默認只有一個數據
    //使用方法
    ch <- v    // 將 v 發送至信道 ch。
    v := <-ch  // 從 ch 接收值並賦予 v。
    
    //例子
    package main
    
    import "fmt"
    
    func sum(s []int, c chan int) {
    	sum := 0
    	for _, v := range s {
    		sum += v
    	}
    	c <- sum // 將和送入 c
    }
    
    func main() {
    	s := []int{7, 2, 8, -9, 4, 0}
    
    	c := make(chan int)
    	go sum(s[:len(s)/2], c)
    	go sum(s[len(s)/2:], c)
    	x, y := <-c, <-c // 從 c 中接收
    
    	fmt.Println(x, y, x+y)
    }
    
  • 帶緩衝的channel:

    //初始化帶緩存的信道
    ch := make(chan int, 100)
    
    //當信道填滿後會阻塞
    package main
    
    import "fmt"
    
    func main() {
    	ch := make(chan int, 2)
    	ch <- 1
    	ch <- 2
    	fmt.Println(<-ch)
    	fmt.Println(<-ch)
    }
    
  • range和close

    • 發送者關閉close,只能由發送者關閉。如 close©.

    • 接收者通過表達式檢測信道是否關閉。如 v,ok := <-ch.

    • 信道與文件不同,只有告訴發送者不再接受時才能不關閉。

      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)
      }
      
      func main() {
      	c := make(chan int, 10)
      	go fibonacci(cap(c), c)
      	for i := range c {
      		fmt.Println(i)
      	}
      }
      
  • select 語句:

    • 使得go等待多個通信操作。多個分支都準備好時隨機選擇一個執行;

      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
      		}
      	}
      }
      
      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)
      }
      
    • 當其他分支都沒準備好時,default分支就會執行

    //model
    select {
    case i := <-c:
        // 使用 i
    default:
        // 從 c 中接收會阻塞時執行
    }
    
    //example
    package main
    
    import (
    	"fmt"
    	"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)
    		}
    	}
    }
    
    
  • asycn.Mutex互斥鎖:

    • 比如:想保證每次只有一個Go程能夠訪問一個共享的變量

    • Lock

    • Unlock

      package main
      
      import (
      	"fmt"
      	"sync"
      	"time"
      )
      
      // SafeCounter 的併發使用是安全的。
      type SafeCounter struct {
      	v   map[string]int
      	mux sync.Mutex
      }
      
      // Inc 增加給定 key 的計數器的值。
      func (c *SafeCounter) Inc(key string) {
      	c.mux.Lock()
      	// Lock 之後同一時刻只有一個 goroutine 能訪問 c.v
      	c.v[key]++
      	c.mux.Unlock()
      }
      
      // Value 返回給定 key 的計數器的當前值。
      func (c *SafeCounter) Value(key string) int {
      	c.mux.Lock()
      	// Lock 之後同一時刻只有一個 goroutine 能訪問 c.v
      	defer c.mux.Unlock()
      	return c.v[key]
      }
      
      func main() {
      	c := SafeCounter{v: make(map[string]int)}
      	for i := 0; i < 1000; i++ {
      		go c.Inc("somekey")
      	}
      
      	time.Sleep(time.Second)
      	fmt.Println(c.Value("somekey"))
      }
      
  • Reference:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章