My journey to learn GO :Day3
指针
- Go拥有指针。指针保存了值的内存地址
类型*T是指向T类的指针。其零值为nil(相当于其他语言中的NULL)
var p *int
&操作符会生成一个指向其操作数的指针
i := 42
p = &i
*操作符表示指针指向的底层值
fmt.Println(*p)
*p = 21
- Go没有指针运算
i, j := 1, 2
p := &i // p 是指向 i 的指针
fmt.Println(*p) // 通过指针 p 读取 i 的值(*p)
*p = 21 // 通过指针 p 设置 i 的值(*p)
fmt.Println(i) // 查看 i 的值
p = &j // 指向 j
// p += p 报错:(operator + not defined on pointer)
*p = *p / 37 // 通过指针p对 j 进行除法运算
fmt.Println(j) // 查看 j 的值
结构体
- 一个结构体(struct)就是一个字段(field)
type Vertex struct {
X int
Y int
}
- 结构体字段使用点号
.来访问
v := Vertex{1, 2}
v.X = 4
- 结构体指针:允许使用隐式间接调用
(*p).X省略为p.X
v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
- 结构体文法:可以使用Name:语法可以仅列出部分字段(字段名的顺序无关) 特殊的前缀&返回一个指向结构体的指针
type Vertex struct {
X,Y int
}
var (
v1 = Vertex{1, 2}
v2 = Vertex{X: 1}//通过Name:来命名 Y:0被隐式赋予
v3 = Vertex{}//X:0 Y:0
p = &Vertex{1,2} //创建一个 *Vertex 类型的结构体(指针)
)
数组
- 类型[n]T表示 拥有 n 个 T类型的值的数组
var a [10]int
var a [2]string
a[0] = "Hello"
a[1] = "World"
primes := [6]int{1,2,3,4,5,6}
fmt.Println(a[0],a[1])
fmt.Println(a)
fmt.Println(primes)
切片
[]T表示一个元素类型为T的切片- 切片通过两个下标(上界和下界)来界定
a[low:high]选择一个半开区间,包括第一个元素,但排除最后一个元素(前闭后开)
primes := [6]int{1,2,3,4,5,6}
var s []int = primes[1:4]//s []int 定义类型为int 的切片
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)
- 切片文法类似于没有长度的数组文法
[]bool false true false
q := []int{1, 2, 3, 4, 5, 6}
fmt.Println(q)
r := []bool{false, true, false, true, true}
fmt.Println(r)
s := []struct {
i int
b bool
}{
{1, false},
{2, true},
{3, false},
{4, true},
}
fmt.Println(s)
- 切片下界的默认值为0,上界则是该切片的长度。比如:
var a[10] int等价于a[0:10]a[:10]a[0:]a[:]
s := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Println(s)
s = s[1:4]
fmt.Println(s)
s = s[:2]
fmt.Println(s)
s = s[1:]
fmt.Println(s)
- 切片有长度(切片包含的元素个数)和容量(切片底层数组的元素个数),获取切片
s的长度len(s)和容量cap(s)
func printSlice(s []int) {
fmt.Printf("len= %d cap= %d %v\n", len(s), cap(s), s)
}
func main() {
s := []int{1, 2, 3, 4, 5, 6}
printSlice(s)
s = s[:0]//截取切片使其长度为0
printSlice(s)
s = s[:6]//拓展使其长度为6
printSlice(s)
s = s[2:]//舍去前两位
printSlice(s)
s = s[:4]//拓展
printSlice(s)
}
- 切片的零值为
nil,nil切片的长度和容量为0没有底层数组
var s[] int
fmt.Println(len(s),cap(s),s)
- 用
make创建切片,表达式name := make([]type, len)或name := make([]type,len, cap)
func printSlice(s string,x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",s,len(x),cap(x),x)
}
func main() {
a := make([]int, 5)
printSlice("a",a)
b := make([]int, 0, 5)
printSlice("b",b)
}
- 切片的切片
import (
"fmt"
"strings"
)
func main() {
board :=[][]string{//切片的切片
[]string{"+","+","+"},
[]string{"+","+","+"},
[]string{"+","+","+"},
}
board[0][0] = "O"
board[1][1] = "O"
board[2][2] = "O"
fmt.Println(len(board))//len(board) = 3
for i := 0;i < len(board);i++ {
fmt.Printf("%s\n",strings.Join(board[i]," "))//stings.Join() 为board[i]中所有元素拼接一个" "
}
}
- 向切片添加元素的函数原型
func append(slice []Type, elems ...Type) []Type,内建函数的文档
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v \n",len(s),cap(s),s)
}
func main() {
var s []int
printSlice(s)
for i:=0;i<10;i++ {
s = append(s, i)
printSlice(s)
}
}
输出结果为:
len=0 cap=0 []
len=1 cap=2 [0]
len=2 cap=2 [0 1]
len=3 cap=4 [0 1 2]
len=4 cap=4 [0 1 2 3]
len=5 cap=8 [0 1 2 3 4]
len=6 cap=8 [0 1 2 3 4 5]
len=7 cap=8 [0 1 2 3 4 5 6]
len=8 cap=8 [0 1 2 3 4 5 6 7]
len=9 cap=16 [0 1 2 3 4 5 6 7 8]
len=10 cap=16 [0 1 2 3 4 5 6 7 8 9]
Range
- for
循环的range形式可遍历切片或映射。使用for`循环遍历切片时每次迭代会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的副本
var s = []int{1,2,3,4,5}
func main() {
for i,v :=range s{//i 为元素下标,v 为 元素值
fmt.Printf("2**%d = %d\n",i,v)
}
}
- 可以赋予
_给下标或值来实现忽略,若只需要索引,忽略第二个变量即可
for i,_ := range s//忽略value
for _,value := range s//忽略i
for i :=range s//只需要索引
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << unit(i)// == 2**i
}
fmt.Printf("下标:\t")
for i, _ := range pow {// for i := range pow
fmt.Printf("%d\t", i)
}
fmt.Printf("\n")
fmt.Printf("值:\t")
for _, value := range pow {
fmt.Printf("%d\t", value)
}
}
切片练习
func Pic(dx, dy int) [][]uint8 {
a := make([][]uint8,dy) //外层切片
for x := range a{
b :=make([]uint8,dx) //内存切片
for y := range b{
b[y] = uint8((x+y)/2)
}
a[x] = b
}
return a
}
映射(和PYTHON的字典有点像)
make函数会返回给定类型的映射
var m map[string] int//定义
m = make(map[string]int)//初始化
m["1"] = 1
fmt.Println(m["1"])
- 映射和PYTHON的字典相似
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
- 修改映射
m[key] = elem可在映射中插入或者修改元素、elem = m[key]获得元素、delete(m,key)删除元素、elem,ok = m[key]检查elem是否在m中,如果在ok为true、否则为false
m := make(map[string]int)//初始化
m["Answer"] = 1
v , ok := m["Answer"] //判断Answer是否在m中
fmt.Println("The value:", v, "Present?", ok)
- 练习:
func WordCount(s string) map[string]int {
m := make(map[string]int)//初始化映射
for _,v := range strings.Fields(s){ //strings.Fields(s)返回一个字符串数组,用 _ 舍去数组下标,取数组的值 v
m[v] +=1
}
return m
}
函数值
- 函数也是值。可以像其他值一样传递,可作为参数或返回值
func compute(fn func(float64, float64) float64) float64 {//函数这个方式做参数,方式传递过来时,用这个方式计算,并返回值
return fn(3, 4)
}
- 函数的闭包:说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕有点绕人,自行参考CSDN上的一篇博客
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 < 3; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
0 0 //每个闭包都绑定了各自的sun变量 1 -2
3 -6
- 斐波纳契闭包练习:
func fibonacci() func() int {
s1:=0
s2:=1
s:=0
return func() int{
s = s1
s1=s1+s2
s2 =s
return s2
}
}