go

go_demo

go_demo

Posted by DYC on December 22, 2024

go_demo

values

Go 有各种值类型,包括字符串、整数、浮点数、布尔值等。这里是一些基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
	fmt.Println("go" + "lang")
	fmt.Println("1+1 =", 1+1)
	fmt.Println("7.0/3.0 =", 7.0/3.0)
	fmt.Println(true && false)
	fmt.Println(true || false)
	fmt.Println(!true)
}

变量

在 Go 中,变量由编译器明确声明和使用,例如检查函数调用的类型正确性

var可以一次声明一个或多个变量,Go将判断初始化变量的类型,未进行初始化的声明变量为零值

:=近可在函数内部使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
    var a = "initial"
    fmt.Println(a)

    var b, c int = 1, 2
    fmt.Println(b, c)

    var d = true
    fmt.Println(d)

    var e int
    fmt.Println(e)

    f := "apple"
    fmt.Println(f)
}
常量

Go支持字符、字符串、布尔值、数字值的常量

const声明一个常量值,用const取代var即可,数字常量没有类型,除非通过显式转换等方式指定类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
    "fmt"
    "math"
)

const s string = "constant"

func main() {
    fmt.Println(s)

    const n = 500000000

    const d = 3e20 / n
    fmt.Println(d)

    fmt.Println(int64(d))

    fmt.Println(math.Sin(n))
}
for

for是 Go 唯一的循环结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

func main() {
    i := 1
    for i <= 3 {
       fmt.Println(i)
       i++
    }
    for j := 0; j < 5; j++ {
       fmt.Println(j)
    }
    nums := []int{1, 2, 3}
    for i := range nums {
       fmt.Println("range", i)
    }

    for {
       fmt.Println("loop")
       break
    }
    for i := 1; i <= 6; i++ {
       if i%2 == 1 {
          continue
       }
       fmt.Println(i)
    }
}
If-else

可以有if一个不带 else 的语句,语句可以位于条件之前,此语句声明任何变量可以在该分支及后续分支使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
	if 7%2 == 0 {
		fmt.Println("7 is even")
	}

	if 8%2 == 0 || 7%2 == 0 {
		fmt.Println("either 8 or 7 are even")
	}
	
	if num := 9; num < 0 {
		fmt.Println(num, "is negative")
	} else if num > 0 {
		fmt.Println(num, "is positive")
	} else {
		fmt.Println(num, "is zero")
	}

}

switch

Switch 语句表达跨多个分支的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
	"fmt"
	"time"
)

func main() {
	i := 2
	fmt.Print("write", i, "as")
	switch i {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	}

	switch time.Now().Weekday() {
  //可以使用逗号分隔同一`case`语句中的多个表达式
	case time.Saturday, time.Sunday:
		fmt.Println("it's the weekend")
	default:
		fmt.Println("it's a weekday")
	}

	t := time.Now()
  //`switch`不使用表达式是表达 if/else 逻辑的另一种方式,
	switch {
	case t.Hour() < 12:
		fmt.Println("it's before noon")
	default:
		fmt.Println("it's after noon")
	}

	whatAmI := func(i interface{}) {
    //类型`switch`比较的是类型而不是值。可以使用它来发现接口值的类型
		switch t := i.(type) {
		case bool:
			fmt.Println("I'm a bool")
		case int:
			fmt.Println("I'm an int")
		case string:
			fmt.Println("I'm a string")
		default:
			fmt.Printf("Don't know type %T\n", t)
		}
	}
	whatAmI(true)
	whatAmI("hey")
	whatAmI(1)
	
}

数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import "fmt"

func main() {
    var a [5]int
    fmt.Println(a)

    a[4] = 100
    fmt.Println(a[4])
    fmt.Println(len(a))

    b := [5]int{1, 2, 3, 4, 5}
    fmt.Println(b)
		//可以让编译器计算元素的数量...
    b = [...]int{1, 2, 3, 4, 5}
    fmt.Println(b)
    //[100 0 0 400 500],使用 指定索引:,则其间的元素将被清零
    b = [...]int{100, 3: 400, 500}
    fmt.Println(b)

    var twoD [2][3]int
    for i := 0; i < len(twoD); i++ {
       for j := 0; j < len(twoD[i]); j++ {
          twoD[i][j] = i + j
       }
    }
    fmt.Println(twoD)
    twoD = [2][3]int{
       {1, 2, 3},
       {2, 3, 4},
    }
    fmt.Println(twoD)

}
切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main

import (
	"fmt"
	"reflect"
	"slices"
)

func main() {
	var s []string
	//[] true true
	fmt.Println(s, s == nil, len(s) == 0)

	s = make([]string, 3)
	//[  ] false 3 3
	fmt.Println(s, s == nil, len(s), cap(s))

	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println(s)
	fmt.Println(s[2])
	fmt.Println(len(s))
	////需要接受返回值, append因为我们可能会得到一个新的切片值
	s = append(s, "d")
	s = append(s, "e", "f")
	//[a b c d e f]
	fmt.Println(s)

	c := make([]string, len(s))
  //这里我们创建一个与 长度相同的copy空切片并将其复制到中
	copy(c, s)
	fmt.Println(c)

	l := s[2:5]
	//[c d e]
	fmt.Println(l)
	//可以在一行中声明并初始化切片的变量
	t := []string{"a", "b", "c"}
	t2 := []string{"a", "b", "c"}
	//t==t2,这个用于判断两个切片中的内容是否相同
	if slices.Equal(t, t2) {
		fmt.Println("t==t2")
	}
	//如果想比较两个的地址的话,可以使用返回或者比较两个的指针,不能直接用t==t2进行比较
	if reflect.ValueOf(t).Pointer() == reflect.ValueOf(t2).Pointer() {
		fmt.Println("t and t2 share the same underlying array")
	} else {
		fmt.Println("t and t2 do not share the same underlying array")
	}
	if &t[0] == &t2[0] {
		fmt.Println("t and t2 share the same underlying array")
	} else {
		fmt.Println("t and t2 do not share the same underlying array")
	}
	//切片可以组成多维数据结构。与多维数组不同,内部切片的长度可以变化
	twoD := make([][]int, 3)
	for i := 0; i < len(twoD); i++ {
		inner := i + 1
		twoD[i] = make([]int, inner)
		for j := 0; j < inner; j++ {
			twoD[i][j] = i + j
		}
	}
	//[[0] [1 2] [2 3 4]]
	fmt.Println(twoD)
}

映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
	"fmt"
	"maps"
)

func main() {
	m := make(map[string]int)
	m["k1"] = 7
	m["k2"] = 13
	//map[k1:7 k2:13]
	fmt.Println(m)

	v1 := m["k2"]
	fmt.Println(v1)

	v3 := m["k3"]
	//0,如果键不存在, 则返回值类型的零值
	fmt.Println(v3)

	fmt.Println(len(m))
	//内置命令delete从映射中删除键/值对
	delete(m, "k2")
	//map[k1:7]
	fmt.Println(m)
	//要从map中删除所有clear键/值对
	clear(m)
	//map[]
	fmt.Println(m)
	//从映射中获取值时,可选的第二个返回值指示映射中是否存在该键。
  //这可用于区分缺失键和具有零值的键(如0或 )""。
  //这里我们不需要值本身,因此我们用空白标识符 _忽略它
	_, prs := m["k2"]
	//false
	fmt.Println(prs)

	n := map[string]int{"foo": 1, "bar": 2}
	fmt.Println(n)
	//map[k:v k:v]请注意,使用 打印时,地图会以表格形式出现fmt.Println
	n2 := map[string]int{"foo": 1, "bar": 2}
	if maps.Equal(n, n2) {
		//true
		fmt.Println("true")
	}
}

函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import "fmt"

func plus(a int, b int) int {
	//Go 需要显式返回,即它不会自动返回最后一个表达式的值
	return a + b
}

func plusPlus(a, b, c int) int {
	return a + b + c
}

func vals() (int, int) {
	//可以多返回值
	return 3, 7
}

func main() {

	res := plus(1, 2)
	fmt.Println(res)

	res = plusPlus(1, 2, 3)
	fmt.Println(res)

	a, b := vals()
	fmt.Println(a, b)
	_, c := vals()
	//7
	fmt.Println(c)

}

闭包

Go 支持匿名函数,可以形成闭包。当你想以内联方式定义函数而不必为其命名时,匿名函数非常有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import "fmt"

func intSeq() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

func main() {
  //每次会创建一个新的状态
	nextInt := intSeq()
	//1
	fmt.Println(nextInt())
	//2
	fmt.Println(nextInt())
	//3
	fmt.Println(nextInt())
	newInt := intSeq()
	//1
	fmt.Println(newInt())
}

递归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func fact(n int) int {
    if n == 0 {
       return 1
    }
    return n * fact(n-1)
}

func main() {
    fmt.Println(fact(5))
    var fib func(n int) int
    fib = func(n int) int {
       if n < 2 {
          return n
       }
       return fib(n-1) + fib(n-2)
    }
    fmt.Println(fib(10))
}
range函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

func main() {
    nums := []int{1, 2, 3}
    sum := 0
    for _, num := range nums {
       sum += num
    }
    fmt.Println(sum)
    for i, num := range nums {
       if num == 3 {
          //2
          fmt.Println(i)
       }
    }
    kvs := map[string]int{"a": 1, "b": 2}
    for k, v := range kvs {
       fmt.Println(k, v)
    }
    for k := range kvs {
       fmt.Println(k)
    }

    for i, c := range "go" {
       fmt.Println(i, c)
    }
}
指针

&i语法给出了 的内存地址i,即 的指针i

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func zeroval(inval int) {
    inval = 0
}

func zeroptr(inptr *int) {
    *inptr = 0
}

func main() {
    i := 1
    fmt.Println(i)

    zeroval(i)
    fmt.Println(i)

    zeroptr(&i)
    fmt.Println(i)

}
strings

在 Go 中,rune 是一个类型,实际上是一个表示 Unicode 码点的整数(本质上是 int32 类型)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    const s = "Zbs"
    //3
    fmt.Println(len(s))
    for i := 0; i < len(s); i++ {
       //Z b s
       fmt.Printf("%c ", s[i])
    }
    fmt.Println()
    //3,用于计算字符串中 Unicode 字符(即 rune) 的数量
    fmt.Println(utf8.RuneCountInString(s))

    //如果没有%c,就会输出ascii的int值
    for idx, value := range s {
       //0 90
       //1 98
       //2 115
       fmt.Println(idx, value)
    }

    for i, w := 0, 0; i < len(s); i += w {
       //用于解码字符串中的下一个 Unicode 字符(rune),并返回该字符以及它的字节宽度
       runValue, width := utf8.DecodeRuneInString(s[i:])
       fmt.Println(width, runValue)
       w = width
       examineRunne(runValue)
    }

}
func examineRunne(r rune) {
    if r == 'b' {
       fmt.Println("b")
    } else if r == 'a' {
       fmt.Println("a")
    }
}
结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import "fmt"

type person struct {
    name string
    age  int
}

func newPerson(name string, age int) *person {
    return &person{name, age}
}

func main() {
    fmt.Println(person{"Bob", 20})
    fmt.Println(person{name: "Bob", age: 20})
    fmt.Println(person{name: "Bob"})
    //&{Bob 20}
    fmt.Println(&person{name: "Bob", age: 20})
    //&{Bob 20}
  	//将新结构体的创建封装在构造函数中是一种惯用做法
    fmt.Println(newPerson("Bob", 20))

    s := person{name: "Bob", age: 20}
    fmt.Println(s.name)

    sp := &s
    //20
    fmt.Println(sp.age)
    sp.age = 30
    //30
    fmt.Println(s.age)

    dog := struct {
       name   string
       isGood bool
    }{
       "name",
       true,
    }
    fmt.Println(dog)
}
方法

Go 自动处理方法调用时值与指针之间的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import "fmt"

type rect struct {
    width, height int
}
//该area方法的接收器类型为*rect
func (r *rect) area() int {
    return r.width * r.height
}
//可以为指针或值接收者类型定义方法。以下是值接收者
func (r rect) perim() int {
    return 2*r.width + 2*r.height
}

func main() {
    r := rect{width: 10, height: 5}

    fmt.Println("area: ", r.area())
    fmt.Println("perim:", r.perim())
		//Go 自动处理方法调用时值与指针之间的转换
    rp := &r
    fmt.Println("area: ", rp.area())
    fmt.Println("perim:", rp.perim())
}
接口

在Go中实现接口,我们只需要实现接口里的所有方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
    "fmt"
    "math"
)

type geometry interface {
    area() float64
    perim() float64
}

type rectt struct {
    width, height float64
}

type circle struct {
    radius float64
}

func (r rectt) area() float64 {
    return r.width * r.height
}
func (r rectt) perim() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}
//如果变量具有接口类型,那么我们可以调用指定接口中的方法
func measure(g geometry) {
    fmt.Println(g)
}

func main() {
    r := rectt{width: 10, height: 5}
    c := circle{radius: 5}
  //circle和结构类型rect都实现了geometry接口,因此我们可以使用这些结构的实例作为的参数measure
    measure(r)
    measure(c)
}
枚举

枚举是一种具有固定数量可能值的类型,每个值都有不同的名称。Go 没有枚举类型作为独特的语言功能,但使用现有的语言习语很容易实现枚举。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import "fmt"

type ServerState int

const (
	//这里通过 iota 创建了四个常量,分别表示服务器的四种状态
	//iota 是 Go 中用于生成常量值的一个特殊关键字,它在每一行常量定义中会自动递增
	StateIdle ServerState = iota
	StateConnected
	StateError
	StateRetrying
)

var stateName = map[ServerState]string{
	StateIdle:      "idle",
	StateConnected: "connected",
	StateError:     "error",
	StateRetrying:  "retrying",
}

func (ss ServerState) String() string {
	return stateName[ss]
}

func main() {
	//这里虽然ServerState是int类型,但不能传int类型进去,否则编译器会报错,因为它们的类型不同。
	ns := transition(StateIdle)
	fmt.Println(ns)

	ns2 := transition(ns)
	fmt.Println(ns2)
}

// transition 模拟服务器的状态转换;它采用现有状态并返回新状态
func transition(s ServerState) ServerState {
	switch s {
	case StateIdle:
		return StateConnected
	case StateConnected, StateRetrying:

		return StateIdle
	case StateError:
		return StateError
	default:
		panic(fmt.Errorf("unknown state: %s", s))
	}
}

结构体嵌入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import "fmt"

type base struct {
    num int
}

func (b base) describe() string {
    return fmt.Sprintf("base with num=%v", b.num)
}

type container struct {
  	//Acontainer 嵌入a base。嵌入看起来像一个没有名称的字段
    base
    str string
}

func main() {
		//当使用文字创建结构时,我们必须明确初始化嵌入;这里嵌入的类型用作字段名称
    co := container{
       base: base{
          num: 1,
       },
       str: "some name",
    }
		//这样就可以直接访问子结构体的子字段 例如co.num
    fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)

    fmt.Println("also num:", co.base.num)
		//由于container嵌入base, 的方法 base也成为 的方法。这里我们 直接container在 上调用嵌入的方法
    fmt.Println("describe:", co.describe())

    type describer interface {
       describe() string
    }
		//嵌入带有方法的结构可用于将接口实现赋予其他结构。这里我们看到 acontainer现在实现了 describer接口,因为它嵌入了base。
    var d describer = co
    fmt.Println("describer:", d.describe())
}
泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import "fmt"

// SlicesIndex 使用了 类型约束,其中 S ~[]E 和 E comparable 是函数的类型参数约束
// S 必须是 切片类型,并且该切片的元素类型是 E
// ~ 运算符在这里的作用是允许类型别名,即允许 S 不仅仅是 []E 这样的原始切片类型,也可以是一个切片类型的类型别名
func SlicesIndex[S ~[]E, E comparable](s S, v E) int {
    //E comparable 限制了传入的元素类型 E 必须是可以通过 == 和 != 运算符进行比较的类型
    for i := range s {
       if v == s[i] {
          return i
       }
    }
    return -1
}

// List List[T any] 是一个泛型链表类型,T 表示链表中节点的元素类型,any 表示可以是任何类型
type List[T any] struct {
    head, tail *element[T]
}

// element[T any] 是链表中的节点类型
type element[T any] struct {
    next *element[T]
    val  T
}
//为链表定义了两个方法
//我们可以像在常规类型上一样在泛型类型上定义方法,但必须保留类型参数。类型是List[T],而不是List
func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
       lst.head = &element[T]{val: v}
       lst.tail = lst.head
    } else {
       lst.tail.next = &element[T]{val: v}
       lst.tail = lst.tail.next
    }
}

func (lst *List[T]) AllElements() []T {
    var elems []T
    for e := lst.head; e != nil; e = e.next {
       elems = append(elems, e.val)
    }
    return elems
}

func main() {
    var s = []string{"foo", "bar", "zoo"}

    fmt.Println("index of zoo:", SlicesIndex(s, "zoo"))

    _ = SlicesIndex[[]string, string](s, "zoo")

    lst := List[int]{}
    lst.Push(10)
    lst.Push(13)
    lst.Push(23)
    fmt.Println("list:", lst.AllElements())
}
range迭代器

迭代器函数将另一个函数作为参数,yield按照惯例调用该函数(但名称可以任意)。它将调用yield我们想要迭代的每个元素,并注意yield的返回值以防可能提前终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import (
	"fmt"
	"iter"
	"slices"
)

type List[T any] struct {
	head, tail *element[T]
}

type element[T any] struct {
	next *element[T]
	val  T
}

func (lst *List[T]) Push(v T) {
	if lst.tail == nil {
		lst.head = &element[T]{val: v}
		lst.tail = lst.head
	} else {
		lst.tail.next = &element[T]{val: v}
		lst.tail = lst.tail.next
	}
}

func (lst *List[T]) All() iter.Seq[T] {
	return func(yield func(T) bool) {

		for e := lst.head; e != nil; e = e.next {
			if !yield(e.val) {
				return
			}
		}
	}
}
//迭代(Iteration) 不一定依赖于特定的数据结构,甚至不需要是有限的(比如无限序列)
func genFib() iter.Seq[int] {
	return func(yield func(int) bool) {
		a, b := 1, 1

		for {
			if !yield(a) {
				return
			}
			a, b = b, a+b
		}
	}
}

func main() {
	lst := List[int]{}
	lst.Push(10)
	lst.Push(13)
	lst.Push(23)
	//由于List.All返回一个迭代器,我们可以在常规range循环中使用它
	for e := range lst.All() {
		fmt.Println(e)
	}
	//像切片这样的包有许多有用的函数可以与迭代器一起使用
	all := slices.Collect(lst.All())
	fmt.Println("all:", all)
	
	for n := range genFib() {

		if n >= 10 {
			break
		}
		fmt.Println(n)
	}
}

错误