Golang 通关初级(2)

2024-08-26 17:18
文章标签 golang 初级 通关

本文主要是介绍Golang 通关初级(2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


原文

https://tour.golang.org/welcome/1

指针

Go 具有指针。 指针保存了变量的内存地址。

类型 *T 是指向 T 类型值的指针。其零值为 nil 。

var p *int
  • 1
  • 1

& 操作符会生成一个指向其操作数的指针。

i := 42
p = &i
  • 1
  • 2
  • 1
  • 2
  • 操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21         // 通过指针 p 设置 i
  • 1
  • 2
  • 1
  • 2

这也就是通常所说的“间接引用”或“重定向”。

与 C 不同,Go 没有指针运算。

package mainimport ("fmt"
)func main() {a, b := 12, 33p := &afmt.Printf("p = %v, *p = %v\n", p, *p)*p = *p * 2fmt.Printf("p = %v, *p = %v\n", p, *p)p = &bfmt.Printf("p = %v, *p = %v\n", p, *p)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出:

p = 0xc082004258, *p = 12
p = 0xc082004258, *p = 24
p = 0xc082004280, *p = 33
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

结构体

一个结构体( struct )就是一个字段的集合。

(而 type 声明就是定义类型的。)

package mainimport ("fmt"
)type People struct {name stringage  int
}func main() {fmt.Println(People{"Afra", 22})
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出:

{Afra 22}
  • 1
  • 1

结构体字段 
结构体字段使用点号来访问。

package mainimport ("fmt"
)type People struct {name stringage  int
}func main() {me := People{"Afra", 22}fmt.Printf("name = %v, age = %v\n", me.name, me.age)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出:

name = Afra, age = 22
  • 1
  • 1

结构体指针 
结构体字段可以通过结构体指针来访问。

如果我们有一个指向结构体的指针 p ,那么可以通过 (*p).X 来访问其字段 X 。 不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以。

package mainimport ("fmt"
)type People struct {name stringage  int
}func main() {me := People{"Afra", 22}p := &mefmt.Printf("name = %v, age = %v\n", p.name, p.age)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

输出:

name = Afra, age = 22
  • 1
  • 1

结构体文法 
结构体文法通过直接列出字段的值来新分配一个结构体。

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

特殊的前缀 & 返回一个指向结构体的指针。

package mainimport ("fmt"
)type People struct {name stringage  int
}var (a = People{"Afra", 22}b = People{age: 22}c = People{name: "Victor"}d = People{}e = &People{"Victor", 87}
)func main() {fmt.Println(a, b, c, d, e)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

输出:

{Afra 22} { 22} {Victor 0} { 0} &{Victor 87}
  • 1
  • 1

数组

类型 [n]T 表示拥有 n 个 T 类型的值的数组。

表达式

var a [10]int 
会将变量 a 声明为拥有有 10 个整数的数组。

数组的长度是其类型的一部分,因此数组不能改变大小。 这看起来是个限制,不过没关系, Go 提供了更加便利的方式来使用数组。

package mainimport ("fmt"
)func main() {var a [2]stringa[0] = "Afra55"a[1] = "Victor"fmt.Println(a[0], a[1])fmt.Println(a)b := [6]int{1, 2, 4, 5, 6, 6}fmt.Println(b)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

输出:

Afra55 Victor
[Afra55 Victor]
[1 2 4 5 6 6]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

切片

每个数组的大小都是固定的。 而切片则为数组元素提供动态大小的、灵活的视角。 在实践中,切片比数组更常用。

类型 []T 表示一个元素类型为 T 的切片。

以下表达式为数组 a 的前五个元素创建了一个切片。

a[0:5]
  • 1
  • 1
package mainimport ("fmt"
)func main() {source := [6]int{1, 2, 3, 5, 4}var s []int = source[2:6]fmt.Println(s)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出:

[3 5 4 0]
  • 1
  • 1

切片就像数组的引用

切片并不存储任何数据, 它只是描述了底层数组中的一段。

更改切片的元素会修改其底层数组中对应的元素。

与它共享底层数组的切片都会观测到这些修改。

package mainimport ("fmt"
)func main() {source := [6]int{1, 2, 3, 5, 4}var s []int = source[2:6]fmt.Println(s)source[5] = 7fmt.Println(s)s[0] = 88fmt.Println(source)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出:

[3 5 4 0]
[3 5 4 7]
[1 2 88 5 4 7]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

切片文法 
切片文法类似于没有长度的数组文法。

这是一个数组文法:

[3]bool{true, true, false}
  • 1
  • 1

下面这样则会创建一个和上面相同的数组,然后构建一个引用了它的切片:

[]bool{true, true, false}
  • 1
  • 1
package mainimport ("fmt"
)func main() {// a 是切片a := []int{12, 5, 3, 6, 8, 6}fmt.Println(a)// b 是个切片b := []struct {age  intname string}{{1, "xx"}, {2, "xxx"}, {3, "xxxx"},}fmt.Println(b)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

输出:

[12 5 3 6 8 6]
[{1 xx} {2 xxx} {3 xxxx}]
  • 1
  • 2
  • 1
  • 2

切片的默认行为 
在进行切片时,你可以利用它的默认行为来忽略上下界。

切片下界的默认值为 0 ,上界则是该切片的长度。

对于数组:

var a [10]int
  • 1
  • 1

来说,以下切片是等价的:

a[0:10]
a[:10]
a[0:]
a[:]
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
package mainimport ("fmt"
)func main() { // a 是切片a := []int{12, 5, 3, 6, 8, 6}a = a[1:6]fmt.Println(a)a = a[:3]fmt.Println(a)a = a[2:]fmt.Println(a)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出:

[5 3 6 8 6]
[5 3 6]
[6]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

切片的长度与容量 
切片拥有 长度 和 容量 。

切片的长度就是它所包含的元素个数。

切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。

package mainimport ("fmt"
)func main() { // a 是切片a := []int{12, 5, 3, 6, 8, 6} // 让切片的长度为 0a = a[:0]printSlice(a) // 扩充切片的长度a = a[:3]printSlice(a) // 丢掉开始的两个元素a = a[2:]printSlice(a)
}func printSlice(s []int) {fmt.Printf("len = %d, cap = %d, value = %v\n", len(s), cap(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
  • 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

输出:

len = 0, cap = 6, value = []
len = 3, cap = 6, value = [12 5 3]
len = 1, cap = 4, value = [3]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

由上面可见,不管切片怎么变化,他的长度和容量都和他存储的 第一个元素 有关。

nil 切片 
切片的零值是 nil 。

nil 切片的长度和容量为 0 且没有底层数组。

package mainimport "fmt"func main() {var s []intfmt.Println(s, len(s), cap(s))if s == nil {fmt.Println("nil!")}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出:

[] 0 0
nil!
  • 1
  • 2
  • 1
  • 2

用 make 创建切片

切片可以用内建函数 make 来创建,这也是你创建动态数组的方式。

make 函数会分配一个元素为零值的数组并返回一个引用了它的切片:

a := make([]int, 5)  // len(a)=5
  • 1
  • 1

要指定它的容量,需向 make 传入第三个参数:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
package mainimport ("fmt"
)func main() {a := make([]int, 6)printSlice("a", a)b := make([]int, 0, 5)printSlice("b", b)c := make([]int, 3, 5)printSlice("c", c)d := b[:2]printSlice("d", d)e := d[2:5]printSlice("e", e)
}func printSlice(flag string, s []int) {fmt.Printf("%s, len = %d, cap = %d, value = %v\n", flag, len(s), cap(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
  • 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

输出:

a, len = 6, cap = 6, value = [0 0 0 0 0 0]
b, len = 0, cap = 5, value = []
c, len = 3, cap = 5, value = [0 0 0]
d, len = 2, cap = 5, value = [0 0]
e, len = 3, cap = 3, value = [0 0 0]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

切片的切片 
切片可包含任何类型,甚至包括其它的切片。

package mainimport ("fmt""strings"
)func main() { // a是个切片a := [][]string{[]string{"_", "_", "_"}, // 这是个切片[]string{"_", "_", "_"}, // 这是个切片[]string{"_", "_", "_"}, // 这是个切片}printSlice("s", a)a[0][0] = "x"a[2][2] = "x"a[1][2] = "x"a[0][2] = "x"printSlice("s", a)for i := 0; i < len(a); i++ {fmt.Println(strings.Join(a[i], " "))}
}func printSlice(flag string, s [][]string) {fmt.Printf("%s, len = %d, cap = %d, value = %v\n", flag, len(s), cap(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
  • 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

输出:

s, len = 3, cap = 3, value = [[_ _ _] [_ _ _] [_ _ _]]
s, len = 3, cap = 3, value = [[x _ x] [_ _ x] [_ _ x]]
x _ x
_ _ x
_ _ x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

向切片追加元素 
为切片追加新的元素是种常用的操作,为此 Go 提供了内建的 append 函数。 内建函数的文档对此函数有详细的介绍。

func append(s []T, vs …T) []T 
append 的第一个参数 s 是一个元素类型为 T 的切片, 其余类型为 T 的值将会追加到该切片的末尾。

append 的结果是一个包含原切片所有元素加上新添加元素的切片。

当 s 的底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组。 返回的切片会指向这个新分配的数组。

(要了解关于切片的更多内容,请阅读文章Go 切片:用法和本质。)

package mainimport ("fmt"
)func main() {a := []int{1, 2, 3, 4, 5}printSlice(a)a = append(a, 8)printSlice(a)a = append(a, 8, 9, 10, 11, 12)printSlice(a)var b []intprintSlice(b)b = append(b, 0)printSlice(b)b = append(b, 1)printSlice(b)b = append(b, 2)printSlice(b)b = append(b, 3, 4, 5)printSlice(b)
}func printSlice(s []int) {fmt.Printf("len = %d, cap = %d, value = %v\n", len(s), cap(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
  • 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

输出:

len = 5, cap = 5, value = [1 2 3 4 5]
len = 6, cap = 10, value = [1 2 3 4 5 8]
len = 11, cap = 20, value = [1 2 3 4 5 8 8 9 10 11 12]
len = 0, cap = 0, value = []
len = 1, cap = 1, value = [0]
len = 2, cap = 2, value = [0 1]
len = 3, cap = 4, value = [0 1 2]
len = 6, cap = 8, value = [0 1 2 3 4 5]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由上面的输出结果可看,向切片追加元素,新切片的容量以原切片的容量为基数倍增,直到空间能够容乃所有元素。

Range

for 循环的 range 形式可遍历切片或映射。

当使用 for 循环遍历切片时,每次迭代都会返回两个值。 第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。

package mainimport ("fmt"
)var result = []int{1, 2, 4, 8, 16, 32, 64, 128}func main() {printSlice(result)for i, v := range result {fmt.Printf("i = %d, value = %d\n", i, v)}
}func printSlice(s []int) {fmt.Printf("len = %d, cap = %d, value = %v\n", len(s), cap(s), s)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出:

len = 8, cap = 8, value = [1 2 4 8 16 32 64 128]
i = 0, value = 1
i = 1, value = 2
i = 2, value = 4
i = 3, value = 8
i = 4, value = 16
i = 5, value = 32
i = 6, value = 64
i = 7, value = 128
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可以将下标或值赋予 _ 来忽略它。

若你只需要索引,去掉 , value 的部分即可。

package mainimport ("fmt"
)func main() {result := make([]int, 10)printSlice(result)fmt.Println("\n只获取索引:")for i := range result {result[i] = 1 << uint(i) // == 2**ifmt.Printf("i = %d, value = %d\n", i, result[i])}fmt.Println("\n只获取 value:")for _, v := range result {fmt.Printf("value = %d\n", v)}
}func printSlice(s []int) {fmt.Printf("len = %d, cap = %d, value = %v\n", len(s), cap(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
  • 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

输出:

len = 10, cap = 10, value = [0 0 0 0 0 0 0 0 0 0]只获取索引:
i = 0, value = 1
i = 1, value = 2
i = 2, value = 4
i = 3, value = 8
i = 4, value = 16
i = 5, value = 32
i = 6, value = 64
i = 7, value = 128
i = 8, value = 256
i = 9, value = 512只获取 value:
value = 1
value = 2
value = 4
value = 8
value = 16
value = 32
value = 64
value = 128
value = 256
value = 512
  • 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
  • 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

映射

映射将键映射到值。

映射的零值为 nil 。nil 映射既没有键,也不能添加键。

make 函数会返回给定类型的映射,并将其初始化备用。

package mainimport ("fmt"
)type People struct {age  intname string
}var m map[string]Peoplefunc main() {m = make(map[string]People)fmt.Println(m)m["Afra55"] = People{ 22, "Victor",}fmt.Println(m)fmt.Println(m["Afra55"])
}
  • 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
  • 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

输出:

map[]
map[Afra55:{22 Victor}]
{22 Victor}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

映射的文法

映射的文法与结构体相似,不过必须有键名。

package mainimport ("fmt"
)type People struct {age  intname string
}var m = map[string]People{"Victor": People{12, "LaBabaLa"},"Afra55": People{221, "FFFFFFF"},
}func main() {fmt.Println(m)m["Afra55"] = People{ 22, "XXXXXX",}fmt.Println(m)fmt.Println(m["Afra55"])
}
  • 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
  • 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

输出:

map[Victor:{12 LaBabaLa} Afra55:{221 FFFFFFF}]
map[Afra55:{22 XXXXXX} Victor:{12 LaBabaLa}]
{22 XXXXXX}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

可以在文法的元素中省略顶级类型名。

var m = map[string]People{"Victor": {12, "LaBabaLa"},"Afra55": {221, "FFFFFFF"},
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

修改映射 
在映射 m 中插入或修改元素:

m[key] = elem
  • 1
  • 1

获取元素:

elem = m[key]
  • 1
  • 1

删除元素:

delete(m, key)
  • 1
  • 1

通过双赋值检测某个键是否存在:

elem, ok = m[key]
  • 1
  • 1

若 key 在 m 中, ok 为 true ;否则, ok 为 false 。

若 key 不在映射中,那么 elem 是该映射元素类型的零值。

同样的,当从 映射 中读取某个不存在的键时,结果是 映射 的元素类型的零值。

注 :若 elem 或 ok 还未声明,你可以使用短变量声明:

elem, ok := m[key]
  • 1
  • 1
package mainimport ("fmt"
)type People struct {age  intname string
}var m = map[string]People{"Victor": {12, "LaBabaLa"},"Afra55": {221, "FFFFFFF"},
}func main() {// 插入元素m["Lazyer"] = People{ 32, "Old man",}fmt.Println(m)// 修改元素m["Afra55"] = People{ 22, "XXXXXX",}fmt.Println(m)// 检测元素是否存在checkExist("Afra55")// 删除元素delete(m, "Afra55")// 检测元素是否存在checkExist("Afra55")
}func checkExist(key string) {elem, ok := m[key]if ok {fmt.Printf("%s is %d\n", key, elem)} else {fmt.Printf("%s not exist, the value is %v\n", key, elem)}
}
  • 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
  • 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

输出:

map[Lazyer:{32 Old man} Victor:{12 LaBabaLa} Afra55:{221 FFFFFFF}]
map[Victor:{12 LaBabaLa} Afra55:{22 XXXXXX} Lazyer:{32 Old man}]
Afra55 is {22 %!d(string=XXXXXX)}
Afra55 not exist, the value is {0 }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

函数值

函数也是值。它们可以像其它值一样传递。

函数值可以用作函数的参数或返回值。

package mainimport ("fmt""math"
)func main() {toSqrt := func(x, y float64) float64 {return math.Sqrt(x*x + y*y)}fmt.Println(toSqrt(12, 5))fmt.Println(autoSqrt(toSqrt))fmt.Println(autoSqrt(math.Pow))}func autoSqrt(fn func(x, y float64) float64) float64 {return fn(4, 3)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

输出:

13
5
64
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

函数的闭包

Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。 该函数可以访问并赋予其引用的变量的值,换句话说,该函数被“绑定”在了这些变量上。

例如,函数 adder 返回一个闭包。每个闭包都被绑定在其各自的 sum 变量上。

package mainimport ("fmt"
)func main() {a := adder()fmt.Println(a)for i := 0; i < 10; i++ {fmt.Println(a(i))}b := adder()fmt.Println(b)for i := 0; i < 10; i++ {fmt.Println(b(i * -1))}
}func adder() func(int) int {sum := 0fmt.Println("sum is init: ", sum)return func(x int) int { // 这就是闭包sum += xreturn sum}
}
  • 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
  • 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

输出:

sum is init:  0
0x401660
0
1
3
6
10
15
21
28
36
45
sum is init:  0
0x401660
0
-1
-3
-6
-10
-15
-21
-28
-36
-45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

练习:斐波纳契闭包

让我们用函数做些好玩的事情。

实现一个 fibonacci 函数,它返回一个函数(闭包), 该闭包返回一个斐波纳契数列 (0, 1, 1, 2, 3, 5, ...) 。

package mainimport "fmt"// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {a, b:=0, 1return func() int{b, a = b+a, breturn a}
}func main() {f := fibonacci()for i := 0; i < 10; i++ {fmt.Println(f())}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

输出:

1
1
2
3
5
8
13
21
34
55
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这篇关于Golang 通关初级(2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1109188

相关文章

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Golang的CSP模型简介(最新推荐)

《Golang的CSP模型简介(最新推荐)》Golang采用了CSP(CommunicatingSequentialProcesses,通信顺序进程)并发模型,通过goroutine和channe... 目录前言一、介绍1. 什么是 CSP 模型2. Goroutine3. Channel4. Channe

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

Golang使用etcd构建分布式锁的示例分享

《Golang使用etcd构建分布式锁的示例分享》在本教程中,我们将学习如何使用Go和etcd构建分布式锁系统,分布式锁系统对于管理对分布式系统中共享资源的并发访问至关重要,它有助于维护一致性,防止竞... 目录引言环境准备新建Go项目实现加锁和解锁功能测试分布式锁重构实现失败重试总结引言我们将使用Go作

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Golang进程权限调度包runtime

关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行GOMAXPROCS:设置最大的可同时使用的 CPU 核数Goexit:退出当前 goroutine(但是defer语句会照常执行)NumGoroutine:返回正在执行和排队的任务总数GOOS:目标操作系统NumCPU:返回当前系统的 CPU 核数量 p

Golang 网络爬虫框架gocolly/colly(五)

gcocolly+goquery可以非常好地抓取HTML页面中的数据,但碰到页面是由Javascript动态生成时,用goquery就显得捉襟见肘了。解决方法有很多种: 一,最笨拙但有效的方法是字符串处理,go语言string底层对应字节数组,复制任何长度的字符串的开销都很低廉,搜索性能比较高; 二,利用正则表达式,要提取的数据往往有明显的特征,所以正则表达式写起来比较简单,不必非常严谨; 三,使

Golang网络爬虫框架gocolly/colly(四)

爬虫靠演技,表演得越像浏览器,抓取数据越容易,这是我多年爬虫经验的感悟。回顾下个人的爬虫经历,共分三个阶段:第一阶段,09年左右开始接触爬虫,那时由于项目需要,要访问各大国际社交网站,Facebook,myspace,filcker,youtube等等,国际上叫得上名字的社交网站都爬过,大部分网站提供restful api,有些功能没有api,就只能用http抓包工具分析协议,自己爬;国内的优酷、

Golang网络爬虫框架gocolly/colly(三)

熟悉了《Golang 网络爬虫框架gocolly/colly 一》和《Golang 网络爬虫框架gocolly/colly 二》之后就可以在网络上爬取大部分数据了。本文接下来将爬取中证指数有限公司提供的行业市盈率。(http://www.csindex.com.cn/zh-CN/downloads/industry-price-earnings-ratio) 定义数据结构体: type Zhj

Golang支持平滑升级的HTTP服务

前段时间用Golang在做一个HTTP的接口,因编译型语言的特性,修改了代码需要重新编译可执行文件,关闭正在运行的老程序,并启动新程序。对于访问量较大的面向用户的产品,关闭、重启的过程中势必会出现无法访问的情况,从而影响用户体验。 使用Golang的系统包开发HTTP服务,是无法支持平滑升级(优雅重启)的,本文将探讨如何解决该问题。 一、平滑升级(优雅重启)的一般思路 一般情况下,要实现平滑