GoLang

标准库 : https://go-zh.org/pkg/ 函数 基本类型 循环 判断 循环和函数练习 switch defer 指针 结构体 数组 切片练习 实现 Pic 。它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx ,元素类型为 uint8 的切片。当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对应的图像。 图像的选择由你来定。几个有趣的函数包括 x+y /2 …

标准库 : 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