簡單例子
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: