My journey to learn GO :Day3

161 阅读6分钟

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