标准库 : https://go-zh.org/pkg/

函数

package main
import "fmt"函数

// 函数声明
func add(x int, y int) int {
return x + y
}
// 变量声明, var用于声明一个变量列表
var c, python, java bool

func main(){
fmt.Println(add(42, 13))
fmt.Println(add2(42, 13))
a, b := swap("Hello", "World")
fmt.Println(a, b)
// 变量声明
var i int

// 变量的初始化, 省略了类型声明,变量自动从初始值中获取类型
var c, python, java = true, false, "no!"

// 短变量声明,可以在类型明确的地方代替var声明
k := 3

}

// 省略声明相同的类型
func add2(x, y int) int {
return x + y
}
// 多值返回
func swap(x, y string) (string, string) {
return y, x
}

基本类型

bool

string

int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的别名

rune // int32 的别名
// 表示一个 Unicode 码点

float32 float64

complex64 complex128
package main

import (
"fmt"
"math/cmplx"
"math"
)

// 分组语法块声明变量
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)

const Pi = 3.14

func main() {
fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
fmt.Printf("Type: %T Value: %v\n", z, z)

// 未明确初始化的变量会被赋予0值
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)

// 类型转换需要显式声明
x, y := 3, 4
m := math.Sqrt(float64(x*x + y*y))
n := uint(m)
fmt.Println(x, y, n)

// 类型推导

z1 := 4
fmt.Printf("z is of type %T\n", z1)
z2 := 1.23
fmt.Printf("z is of type %T\n", z2)
z3 := true
fmt.Printf("z is of type %T\n", z3)

// 常量声明 const, 不能用:=
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Hello", Pi, "Day")

const Truth = true
fmt.Println("Go rules?", Truth)


}
package main
import "fmt"

// 数值常量是高精度的值
const (
Big = 1 << 100
Small = Big >> 99
)

func needInt(x int) int {return x * 10 + 1}
func needFloat(x float64) float64 {
return x * 0.1
}

func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}

循环

package main
import "fmt"

// go 中只有for循环这一种循环结构
// 初始化语句:在第一次迭代前执行
// 条件表达式:在每次迭代前求值
// 后置语句:在每次迭代的结尾执行
func main(){
sum := 0
for i := 0; i < 10; i++{
sum += i
}
fmt.Println(sum)

// 初始化语句和后置语句是可选的
sum = 1
for ; sum < 1000 ; {
sum += sum
}
fmt.Println(sum)

// 上面的去掉分号,就是go中的for循环
sum = 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)

// 无限循环, 省略循环条件

// for {

// }


}

判断

package main

import (
"fmt"
"math"
)

func sqrt(x float64) string {
// 可以省略()
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}


func pow(x, n, lim float64) float64 {
// if 可以在条件表达式之前执行一个简单的语句
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// 这里开始就不能使用 v 了
return lim
}

func main() {
fmt.Println(sqrt(2), sqrt(-4))

fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}

循环和函数练习

package main

import (
"fmt"
)

func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 10; i++ {
z -= (z * z - x) / (2 * z)
fmt.Println(z)
}
return z;
}

func main() {
fmt.Println(Sqrt(4))
}

switch

package main

import (
"fmt"
"runtime"
"time"
)

func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}

// switch中的case语句从上到下执行,直到匹配成功停止
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
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.")
}

// 没有条件的switch相当于 switch true
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}

defer

package main

import "fmt"

func main() {



fmt.Println("counting")

for i := 0; i < 10; i++ {
// defer 会将函数压入栈中,外层函数返回后依次出栈执行
defer fmt.Println(i)
}

fmt.Println("done")

// defer会将函数推迟到外层函数返回之后在执行

defer fmt.Println("world")

fmt.Println("hello")

}

指针

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 的值
}

结构体

package main

import "fmt"

// 一个结构体(struct)就是一组字段(field)。
type Vertex struct {
X int
Y int
}

// 使用 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(Vertex{1, 2})
// 结构体字段使用点号来访问。
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
// 如果我们有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,
// 所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以
p := &v
p.X = 1e9
fmt.Println(v)
}

数组

package main

import "fmt"

func main() {
// 类型 [n]T 表示拥有 n 个 T 类型的值的数组。
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)

// [low:high]切片 [ )左闭右开
var s []int = primes[1:4]
fmt.Println(s)

/* 切片就像数组的引用
切片并不存储任何数据,它只是描述了底层数组中的一段。
更改切片的元素会修改其底层数组中对应的元素。
与它共享底层数组的切片都会观测到这些修改。 */
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)

a := names[0:2]
b := names[1:3]
fmt.Println(a, b)

b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)

// 切片文法 []type{...} 数组文法 []type{...} 直接创建切片相当于先创建一个相同的数组,然后得到该数组的引用切片
// 切片下界的默认值是0 上界的默认值的该切片的长度 假设 a [10]int 那么 [:]、[0:]、[:10]、[0:10]是等价的
// 切片的长度是切片中含有的元素个数,切片的容量是切片中第一个元素到底层数组元素末尾的个数
// 使用len()获取切片长度,cap()获取切片容量
// 切片的零值是nil,长度为0,容量为0
// make([]type, len)
// append
//

s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)

// 截取切片使其长度为 0
s = s[:0]
printSlice(s)

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

// 舍弃前两个值
s = s[2:]
printSlice(s)

// range形式的for循环
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
// 当使用 for 循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}

pow := make([]int, 10)
// 可以将下标或值赋予 _ 来忽略它。
// 若你只需要索引,忽略第二个变量即可。
for i := range pow {
pow[i] = 1 << uint(i) // == 2**i
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}

}

func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

切片练习

实现 Pic。它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片。当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对应的图像。

图像的选择由你来定。几个有趣的函数包括 (x+y)/2, x*y, x^y, x*log(y)x%(y+1)

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
ans := make([][]uint8, dy)
for i := range ans {
ans[i] = make([]uint8, dx)
for j := range ans[i] {
ans[i][j] = (uint8)((i + j) / 2)
}
}
return ans
}

func main() {
pic.Show(Pic)
}

映射

类似于键值对

package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m map[string]Vertex

// 映射的文法与结构体相似,不过必须有键名。
// 结构体在初始化的时候字段可以省略
var n = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}

func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}


var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
// 若顶级类型只是一个类型名,你可以在文法的元素中省略它。
var n = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}

修改映射

package main

import "fmt"

func main() {
m := make(map[string]int)

m["Answer"] = 42
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48
fmt.Println("The value:", m["Answer"])

delete(m, "Answer")
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}

函数值

package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
// 闭包保存了sum的值
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

方法

package main

import (
"fmt"
"math"
)

type Vertex struct {
X, Y float64
}

// 方法是一类带有特殊的接受参数的函数
// 方法的接收者声明位于func关键字和方法名()之间
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 方法即函数
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 也以为非结构体声明方法
// 接收者的类型定义和方法声明必须在同一包内,不能为内建类型声明方法(int...)
type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
// 指针接收者
// 以指针为接收者的方法被调用的时候,接收者既能为值也能为指针
// 同样,以值为接收者的方法被调用的时候,接收者既能为值也能为指针

// 使用指针接收者的原因有二:

// 首先,方法能够修改其接收者指向的值。

// 其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}

// 方法与指针重定向
// 带指针参数的函数必须接收一个指针
// 参数为值的函数必须接收一个值
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}



func main() {
a := Vertex{3, 4}
var f MyFloat
f = 0.31
fmt.Println(a.Abs())
fmt.Println(Abs(a))
fmt.Println(f.Abs())

// 使用值接收者的方法会对接收者中的值的副本进行操作,不会修改接收者中的值
// 使用指针接收者方法,会修改接收者中的值
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs()) // 5 -> 50
}

接口

package main

import (
"fmt"
"math"
)

// 接口类型 是由一组方法签名定义的集合。

// 接口类型的变量可以保存任何实现了这些方法的值。
type Abser interface {
Abs() float64
}

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())
}

type MyFloat float64

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)
}

隐式实现

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()
}

接口值

package main

import (
"fmt"
"math"
)
// 接口可以作为值,充当函数的参数或者返回值
// 在内部,接口值可以看作包含值与具体类型的元组 (value, type)
// 接口调用方法的时候会执行其具体类型的同名方法
// 可以用于类比Java中的接口,实现了go接口中的全部方法就相当于实现了这个接口,可以用这个接口来引用具体类型
// 或者说实现了go接口的全部方法的类型变量就相当于Java中实现了接口的实例
// 即便接口内部的具体值为nil,方法依然会被接口调用
type I interface {
M()
}

type T struct {
S string
}

func (t *T) M() {
fmt.Println(t.S)
}

type F float64

func (f F) M() {
fmt.Println(f)
}

func main() {
var i I

i = &T{"Hello"}
describe(i)
i.M()

i = F(math.Pi)
describe(i)
i.M()
}

func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}

空接口

package main

import "fmt"

func main() {
// 指定了零个方法的接口值被称为 *空接口:*
var i interface{}
describe(i)
// 空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)
i = 42
describe(i)

i = "hello"
describe(i)

// 空接口被用来处理未知类型的值。例如,fmt.Print 可接受类型为 interface{} 的任意数量的参数。
}

func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}

类型断言

package main

import "fmt"

func main() {
var i interface{} = "hello"

s := i.(string)
fmt.Println(s)

s, ok := i.(string)
fmt.Println(s, ok)

f, ok := i.(float64)
fmt.Println(f, ok)

f = i.(float64) // 报错(panic)
fmt.Println(f)
}

类型选择

package main

import "fmt"

func do(i interface{}) {
// 类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}

func main() {
do(21)
do("hello")
do(true)
}

Stringer

package main

import "fmt"

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)
}

错误

package main

import (
"fmt"
"time"
)


// 与 fmt.Stringer 类似,error 类型是一个内建接口:
// type error interface {
// Error() string
// }

type MyError struct {
When time.Time
What string
}

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

// 只有实现了Error() string方法,才能返回error
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}

func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}

错误练习

package main

import (
"fmt"
"math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
if e < 0 {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
return ""
}


func Sqrt(x float64) (float64, error) {
if x >= 0 {
return math.Sqrt(x), nil
} else {
return x, ErrNegativeSqrt(x)
}
}

func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}

Reader

package main

import (
"fmt"
"io"
"strings"
)

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
}
}
}

练习

package main

import (
"io"
"os"
"strings"
)

type rot13Reader struct {
r io.Reader
}

func (rot13 rot13Reader) Read(b []byte) (int, error) {
n, err := rot13.r.Read(b)
for i := 0; i < n; i++ {
c := b[i]
if c >= 'A' && c <= 'Z' {
c = ((c - 'A') + 13) % 26 + 'A'
} else if c >= 'a' && c <= 'z' {
c = ((c - 'a') + 13) % 26 + 'a'
}
b[i] = c
}
return n, err
}

func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}

图像

image 包定义了 Image 接口:

package image

type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}

文档 : https://go-zh.org/pkg/image/#Image

GO的多线程

Thread

go function

信道

信道在使用前必须创建

ch := make(chan int)

ch <- v

v := <-ch