官方doc:English 中文
Go指南:https://tour.go-zh.org/list
包
"math/rand"
包中的源码均以 package rand
语句开始导出名
函数
变量类型在声明之后,why
连续多个参数类型相同时,除最后一个外,其它可以不写func add(x, y int) int ...
可以对返回值命名,需要在函数体内写入返回的变量名,然后返回空值
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
函数值:函数也是值。它们可以像其它值一样传递。
支持闭包
package main
import "fmt"
// 返回一个“返回int的函数”
func fibonacci() func() int {
a := 0
b := 1
return func() int {
ret := a
a = b
b = ret + b
return ret
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
变量
var name type
var x int = 3
or var x = 1
对于赋值的初始化,类型能省略不写,因为能够推测出来:=
可在类型明确的地方代替 var
声明基本类型
bool
string
// int、uint、uintptr追随系统,32位系统为32位,64位系统为64位
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名
// 表示一个 Unicode 码点
float32 float64
complex64 complex128
var i int
T(v)
将值 v
转换为类型 T
const
引导,不能使用短变量声明for
只有for循环=。=
for i := 0; i < 10; i++ {
fmt.Println(i)
}
for {
// 无限循环
}
后置语句(上例的i++)是可选的
if
无需小括号,大括号必须
也拥有初始化语句
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
fmt.Println(v)
return v
}
return lim
}
switch:只运行选定的case,而非之后所有的。
func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
// switch {} 不加变量时,默认是True
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.")
}
}
defer
外围函数执行完毕才执行该函数。推迟的函数调用会被压入一个栈中
defer fmt.Println("world")
fmt.Println("hello")
more: Defer, Panic, and Recover
Defer可以确保程序结束之后的工作正常进行,让coder在开始操作之后立即编写结束代码。配合panic
和recover
可以对异常进行人为的处理。(感觉怎么像是对于大面积try catch的优雅改造)
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i)) // 返回异常信息,4
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
指针
结构体
type Vertex struct {
X int
Y int
}
v := Vertex{1, 2}
v.X = 20
var (
v1 = Vertex{1, 2} // 创建一个 Vertex 类型的结构体
v2 = Vertex{X: 1} // Y:0 被隐式地赋予
v3 = Vertex{} // X:0 Y:0
p = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)
)
数组
[n]T
表示拥有 n
个 T
类型的值的数组。primes := [6]int{2, 3, 5, 7, 11, 13}
or primes := [...]int{2, 3, 5, 7, 11, 13}
切片
array[low: high],数组的引用。
切片文法:primes := []int{2, 3, 5, 7, 11, 13}
,先创建了上方的数组,然后primes是上方数组的引用
长度和容量:len(array)
and cap(array)
。容量是从它的第一个元素开始数,到其底层数组元素末尾的个数,长度就是它所包含的元素个数。
nil切片:长度和容量都为0的切片
make创建切片(动态数组):make(array type, initial length, initial cap)
切片的切片(多维数组)
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
追加元素:func append(s []T, vs ...T) []T
var s []int; s = append(s, 2, 3, 4)
...
语法进行参数展开:var x []int{1, 2}; var y []int{3}; var z = append(y, x...)
range
for
循环的 range
形式可遍历切片或映射。当使用 for
循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。for index, value := range slice {}
映射
映射将键映射到值。
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
// 若顶级类型只是一个类型名,你可以在文法的元素中省略它。
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
m = make(map[string]Vertex) // 返回给定类型的映射,并将其初始化备用
删除元素 delete(map, key)
双赋值检测元素是否存在 elem, ok = m[key]
方法
方法就是一类带特殊的 接收者 参数的函数,方法接收者在它自己的参数列表内,位于 func
关键字和方法名之间
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 指针接收者,更常用,可以修改值
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
方法只是个带接收者参数的函数
接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法
指针重定向:由于 Scale
方法有一个指针接收者,为方便起见,Go 会将语句 v.Scale(5)
解释为 (&v).Scale(5)
;方法调用 p := &v; p.Abs()
会被解释为 (*p).Abs()
尽量使用指针接收者,然后避免混用指针和值接收者的情况。
接口
接口类型 是由一组方法签名定义的集合
type Abser interface {
Abs() float64
}
type MyFloat float64
// 接口隐式实现:此方法表示类型 MyFloat 实现了接口Abser,但我们无需显式声明此事。
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)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 实现了 Abser
a = &v // a *Vertex 实现了 Abser
// 下面一行,v 是一个 Vertex(而不是 *Vertex)
// 所以没有实现 Abser,会报错
a = v
fmt.Println(a.Abs())
}
接口值:接口可以像值一样传递,作为函数的参数或返回值。在内部,接口值可以看做包含值和具体类型的元组(value, type)
。type I interface {..}; var i I; fmt.Printf("(%v, %T)\n", i, i)
空接口:零个方法的接口值被称作空接口。可以保存任何类型的值,因为每个类型都至少实现了零个方法,。可以用来接收未知类型的参数func print(i interface{}) {..}
类型断言
t := i.(T)
该语句断言接口值 i
保存了具体类型 T
,并将其底层类型为 T
的值赋予变量 t
。如果失败,会触发恐慌t, ok := i.(T)
如果失败,t为类型T的零值,ok为false,不会触发恐慌类型选择
类型选择 是一种按顺序从几个类型断言中选择分支的结构。类似于switch
switch v := i.(type) {
case T:
// v 的类型为 T
case S:
// v 的类型为 S
default:
// 没有匹配,v 与 i 的类型相同
}
Stringer
fmt
包中定义的 Stringer
是最普遍的接口之一。
/*
type Stringer interface {
String() string
}
*/
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)
}
错误
Go 程序使用 error
值来表示错误状态。和fmt.Stringer类似,是一个内建接口
type error interface {
Error() string
}
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
调用的它的代码应当判断这个错误是否等于 nil
来进行错误处理
Reader
io
包指定了 io.Reader
接口,它表示从数据流的末尾进行读取。有一个 Read
方法:func (T) Read(b []byte) (n int, err error)
图像
image
包定义了 Image
接口
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
Go程
go func
即可将func的执行放入新的Go程中信道
信道是带有类型的管道,你可以通过它用信道操作符 <-
来发送或者接收值
ch := make(chan int[, int]) // 创建信道,后面为信道的缓冲长度,当长度满后,向信道发送回阻塞
ch <- v // 将 v 发送至信道 ch。
v := <-ch // 从 ch 接收值并赋予 v。
v, ok := <-ch // 通过close关键字关闭信道,可以给接收表达式分配第二个参数来看信道是否关闭
for i := range(ch) {} // 会不断读取信道,直到信道关闭
select
select
语句使一个 Go 程可以等待多个通信操作,select
会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行
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
default:
fmt.Println("default") // 所有select分支都没准备好时
}
}
}
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)
sync.Mutex
互斥锁
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"))
}
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 10; i++ {
z -= (z * z - x) / (2 * z)
}
return z
}
func main() {
fmt.Println(Sqrt(2))
}
package main
import "golang.org/x/tour/pic"
import "math"
func Pic(dx, dy int) [][]uint8 {
ret := make([][]uint8, dy)
for y := range(ret) {
ret[y] = make([]uint8, dx)
for x := range(ret[y]) {
//ret[y][x] = uint8((x + y) / 2)
//ret[y][x] = uint8(x * y)
//ret[y][x] = uint8(x^y)
//ret[y][x] = uint8(math.Pow(float64(x), float64(y)))
ret[y][x] = uint8(float64(x) * math.Log(float64(y)))
//ret[y][x] = uint8(x % (y + 1))
}
}
return ret
}
func main() {
pic.Show(Pic)
}
package main
import (
"golang.org/x/tour/wc"
"strings"
"fmt"
)
func WordCount(s string) map[string]int {
wc := make(map[string]int)
for _, word := range(strings.Fields(s)) {
fmt.Println(word, wc[word])
wc[word] += 1
}
return wc
}
func main() {
wc.Test(WordCount)
}
package main
import "fmt"
// 返回一个“返回int的函数”
func fibonacci() func() int {
a := 0
b := 1
return func() int {
ret := a
a, b = b, a + b
return ret
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
package main
import "fmt"
type IPAddr [4]byte
// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (i IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", i[0], i[1], i[2], i[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
// 不把e转换为float64,那么这条句子其实是
// fmt.Sprint("cannot Sqrt negative number: %v", e.Error())
// 造成死循环
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return -1, ErrNegativeSqrt(x)
}
z := 1.0
for i := 0; i < 10; i++ {
z -= (z * z - x) / (2 * z)
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法
func (r MyReader) Read(data []byte) (int, error) {
for i := range(data) {
data[i] = 'A'
}
return len(data), nil
}
func main() {
reader.Validate(MyReader{})
}
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (rot *rot13Reader) Read(b []byte) (int, error) {
_, err := rot.r.Read(b)
if err != nil {
return 0, err
}
for i, v := range(b) {
if v >= 'A' && v <= 'Z' {
b[i] = byte((int(v) + 13 - int('A')) % 26 + int('A'))
} else if v >= 'a' && v <= 'z' {
b[i] = byte((int(v) + 13 - int('a')) % 26 + int('a'))
}
}
return len(b), nil
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
package main
import "golang.org/x/tour/pic"
import "image"
import "image/color"
type Image struct{
w, h int
v uint8
}
func (i *Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i *Image) Bounds() image.Rectangle {
return image.Rect(0, 0, i.w, i.h)
}
func (i *Image) At(x, y int) color.Color {
return color.RGBA{uint8(x) + i.v, uint8(y) + i.v, 255, 255}
}
func main() {
m := Image{70, 140, 200}
pic.ShowImage(&m)
}
package main
import (
"golang.org/x/tour/tree"
"fmt"
)
/*
type Tree struct {
Left *Tree
Value int
Right *Tree
}
*/
// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
func Walk(t *tree.Tree, ch chan int) {
walk(t, ch)
close(ch)
}
func walk(t *tree.Tree, ch chan int) {
if t != nil {
walk(t.Left, ch)
ch <- t.Value
walk(t.Right, ch)
}
}
// Same 检测树 t1 和 t2 是否含有相同的值。
func Same(t1, t2 *tree.Tree) bool {
tc1 := make(chan int)
tc2 := make(chan int)
go Walk(t1, tc1)
go Walk(t2, tc2)
for v1 := range(tc1) {
if v1 != <- tc2 {
return false
}
}
return true
}
func main() {
fmt.Println(Same(tree.New(1), tree.New(2)))
fmt.Println(Same(tree.New(3), tree.New(3)))
}
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
type SafeUrlCache struct {
v map[string]bool
mux sync.Mutex
}
type Fetcher interface {
// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
Fetch(url string) (body string, urls []string, err error)
}
// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher, c *SafeUrlCache) {
wg.Add(1)
go crawl(url, depth, fetcher, c)
wg.Wait()
}
func crawl(url string, depth int, fetcher Fetcher, c *SafeUrlCache) {
defer wg.Done()
if depth <= 0 {
return
}
c.mux.Lock()
if c.v[url] {
c.mux.Unlock()
return
}
c.v[url] = true
c.mux.Unlock()
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q\n", url, body)
for _, u := range urls {
wg.Add(1)
go crawl(u, depth-1, fetcher, c)
}
return
}
// 等待进程结束,参考:https://www.jianshu.com/p/d55483e90329
func main() {
c := SafeUrlCache{v: make(map[string]bool)}
Crawl("https://golang.org/", 4, fetcher, &c)
}
// fakeFetcher 是返回若干结果的 Fetcher。
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher 是填充后的 fakeFetcher。
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}