本篇本来是接着上一篇的,但是一篇过长,效果不好。上一篇是关于基本语法以及结构的,基本类型、函数、for、if 等等,这里的是关于数据结构以及指针的,下一篇是关于方法、接口以及并发的。
- struct 结构体:
- package main
- import "fmt"
- type Vertex struct {
- X int
- Y int
- }
- func main() {
- fmt.Println(Vertex{1, 2})
- }
1). 使用 type 和 struct 定义结构体;和 c 的类似,结构体是对数据的封装,就是一个容器,里面放一些东东; 2). 结构体中类型声明和普通的一致,先写名字,再写类型; 3). 构造一个结构体值,或是对象,或叫实例,直接名字加参数值即可; 4). 结构体值可以 fmt.Println 直接输出; 5). 实际上可以看出来 go 声明或是定义东西的风格:先说明这是神马玩意,然后是名字,然后具体类型是神马东东;比如变量声明 var a int ,比如函数声明 func add(a int, b int) int ,比如结构体 type Point struct{ x, y int} ;
- 结构体元素访问:
- package main
- import "fmt"
- type Vertex struct {
- X int
- Y int
- }
- func main() {
- v := Vertex{1, 2}
- v.X = 4
- fmt.Println(v)
- }
1). 和 c 类似,直接 v.X 就可以访问,既可以直接使用该值,也可以直接给该值赋值;
- 诡异的指针:
- package main
- import "fmt"
- type Vertex struct {
- X int
- Y int
- }
- func main() {
- p := Vertex{1, 2}
- q := &p
- q.X = 10
- (*q).Y = 20
- r := &q
- //(*r).Y = 4 // ok
- //(*(*r)).X = 100 //ok
- s := &r
- (**s).Y = 30
- fmt.Println(p,q,r,s,*q,*r,*s,**r,**s,***s)
- }
1). 指针在 c 中是个重要的东西,& 和 * 一个取地址、一个解析地址,这是 c 的用法,go 中貌似也是这样,but ! 2). go 中的指针是个“透明”的东西,也就是偶们可以不通过 * 访问,而是直接访问,比如可以不是 (*q).Y = 20 而是 q.Y = 20,有些伤天害理的赶脚! 3). 结构体,结构体指针,结构体指针的指针,结构体指针的指针的指针; 4). 再说明一下:要访问指针 p 指向的结构体中某个元素 x,不需要显式地使用 * 运算,可以直接 p.x ;
- 结构体以及指针类型:
- package main
- import "fmt"
- type Vertex struct {
- X, Y int
- }
- var (
- p = Vertex{1, 2} // has type Vertex
- q = &Vertex{1, 2} // has type *Vertex
- r = Vertex{X: 1} // Y:0 is implicit
- s = Vertex{} // X:0 and Y:0
- )
- func main() {
- fmt.Println(p, q, r, s)
- // fmt.Printf("%T %T %T %T",p, q, r, s) // main.Vertex *main.Vertex main.Vertex main.Vertex
- }
1). 结构体的声明可以简化; 2). 结构体有多种初始化;如果不指定具体的值,也都有默认值; 3). fmt.Printf 格式化输出,可以输出值的类型 %T %v %g;
- new 函数:
- package main
- import "fmt"
- type Vertex struct {
- X, Y int
- }
- func main() {
- v := new(Vertex)
- fmt.Println(v)
- v.X, v.Y = 11, 9
- fmt.Println(v)
- }
1). new 构造结构体,返回指针,看来是借鉴了 c++ 的用法; 2). new 的同时貌似不能初始化,new 是个函数,不像 c++ 中是个运算符;
- map 类型:
- package main
- import "fmt"
- type Vertex struct {
- Lat, Long float64
- }
- var m map[string]Vertex
- func main() {
- m = make(map[string]Vertex)
- m["Bell Labs"] = Vertex{
- 40.68433, 74.39967,
- }
- m["abc"] = Vertex{
- 1.2, 3.4,
- }
- fmt.Println(m["abc"])
- }
1). map 类型就是其他地方说的,hash 表、或是字典、或是关联数组神马的,总之里面就是将 key 和 value 对应起来的对; 2). map 中的 nil 表示为空,不能被赋值; 3). make 函数构造 map,貌似不能通过 new ;
- map 的初始化:
- package main
- import "fmt"
- type Vertex struct {
- Lat, Long float64
- }
- var m = map[string]Vertex{
- "Bell Labs": Vertex{
- 40.68433, -74.39967,
- },
- "Google": Vertex{
- 37.42202, -122.08408,
- },
- }
- func main() {
- fmt.Println(m)
- }
- // 下面是另外一个例子
- package main
- import "fmt"
- type Vertex struct {
- Lat, Long float64
- }
- var m = map[string]Vertex{
- "Bell Labs": {40.68433, -74.39967},
- "Google": {37.42202, -122.08408},
- }
- func main() {
- fmt.Println(m)
- }
1). map 初始化; 2). map 初始化的简化方式;
- map 的用法:
- 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)
- }
1). map 获取某个元素对应的值;修改某个元素对应的值;删除某个元素;
m[key] = elem elem = m[key] delete(m, key) elem, ok = m[key]
- 数组 / 分片类型:
- package main
- import "fmt"
- func main() {
- p := []int{2, 3, 5, 7, 11, 13}
- fmt.Println("p ==", p)
- for i := 0; i < len(p); i++ {
- fmt.Printf("p[%d] == %d\n",
- i, p[i])
- }
- }
1). 数组结构; [] int ; 将类型尽可能想后写,先说明是数组,再说明是 int 数组; 2). len 函数获取数组长度,下标从 0 开始记; 3). 访问方式 p[i] ;
- 子数组:
- package main
- import "fmt"
- func main() {
- p := []int{2, 3, 5, 7, 11, 13}
- fmt.Println("p ==", p)
- fmt.Println("p[1:4] ==", p[1:4])
- // missing low index implies 0
- fmt.Println("p[:3] ==", p[:3])
- // missing high index implies len(s)
- fmt.Println("p[4:] ==", p[4:])
- p[1] = 7
- fmt.Println("p ==", p)
- }
1). 取数组的一部分形成子数组;s[lo:hi] 取 s 中的下表从 lo 开始到 hi-1 结束的部分; 2). 省略前面 lo 部分,表示从 0 开始;省略后面的 hi 部分,到尾结束;s[:] 也是合法的,指的就是 s ; 3). s[lo:lo] 为空;s[lo:lo+1] 只有 s[lo] 一个元素;
- make 构造数组、数组容量:
- package main
- import "fmt"
- func main() {
- a := make([]int, 5)
- printSlice("a", a)
- b := make([]int, 0, 5)
- printSlice("b", b)
- c := b[:2]
- printSlice("c", c)
- d := c[2:5]
- printSlice("d", d)
- a[1] = 2
- //b[1]=4 // index out of range
- c[1]=8
- d[1]=16
- printSlice("a", a)
- printSlice("b", b)
- printSlice("c", c)
- printSlice("d", d)
- }
- func printSlice(s string, x []int) {
- fmt.Printf("%s len=%d cap=%d %v\n",
- s, len(x), cap(x), x)
- }
1). make 函数可以用于构造数组,前面也构造过 map; new 用来构造结构体; 2). 空数组;nil 长度和容量都是 0;nil 还是空 map ;
a := make([]int, 5) // len(a)=5 b := make([]int, 0, 5) // len(b)=0, cap(b)=5 b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4
- 匿名函数:
- package main
- import (
- "fmt"
- "math"
- )
- func main() {
- hypot := func(x, y float64) float64 {
- return math.Sqrt(x*x + y*y)
- }
- fmt.Printf("%T %g",hypot,hypot(3, 4))
- }
1). 函数也是值,可以像普通值那样,传来传去; 2). 匿名函数; 3). 函数的类型,类似于:func(float64,float64) float64
- 函数闭包:
- 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++ {
- fmt.Println(
- pos(i),
- neg(-2*i),
- )
- }
- }
- // result:
- 0 0
- 1 -2
- 3 -6
- 6 -12
- 10 -20
- 15 -30
- 21 -42
- 28 -56
- 36 -72
- 45 -90
1). 在函数式程序设计语言中,闭包都是个重要的玩意; 2). 闭包有时候让人赶脚捉摸不透; 3). 例子中 sum 的作用域、生命周期;实际上在一个函数内部返回一个匿名函数/闭包,如果这个返回函数使用了原来函数内的变量值,那么这些变量的生命周期便延长了;这不是 c 的风格,c 中的管你返回神马东西,局部 auto 变量函数返回后就消亡了(当然你可以使用 static 变量)!
- for - range 结构:
- package main
- import "fmt"
- var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
- func main() {
- for i, v := range pow {
- fmt.Printf("2**%d = %d\n", i, v)
- }
- }
1). 遍历数组或是 map 的一种很方便的方式;不同语言里都有类似结构;foreach 等; 2). 遍历过程中可以获取数组的下标以及值;
- for - range 简化结构:
- package main
- import "fmt"
- func main() {
- pow := make([]int, 10)
- for i := range pow {
- pow[i] = 1<<uint(i)
- }
- for _, value := range pow {
- fmt.Printf("%d\n", value)
- }
- }
1). 简化结构,可以只获取 index 或是只获取 value; 2). 通配符 _ ; 3). 类型转换方法:uint(i);
- switch 结构:
- package main
- import (
- "fmt"
- "runtime"
- )
- 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.", os)
- }
- }
1). switch 分支结构;从上往下匹配,匹配一个处理后便跳出去; 2). 可以添加前置语句; 3). runtime 包;runtime.GOOS 获取操作系统类型; 4). case 可以匹配 string ;匹配执行后主动跳过下面的;fallthrough ;
- 还是 switch 结构:
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- 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.")
- }
- }
- // 下面是另外一个例子
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- t := time.Now()
- fmt.Println(t)
- switch {
- case t.Hour() < 12:
- fmt.Println("Good morning!")
- case t.Hour() < 17:
- fmt.Println("Good afternoon.")
- default:
- fmt.Println("Good evening.")
- }
- }
1). time 时间包;time.Now().Weekday() ;time.Saturday;time.Now().Hour() ; 2). case 类型比较灵活; 3). 如果没有判断条件,则默认 switch true ; 4). switch 可以 添加 default 默认操作,也就是么有其他 case 匹配;
- 练习——求平方根:
- package main
- import (
- "fmt"
- )
- func Sqrt(x float64) float64 {
- z := 1.0
- for i:= 0; i<100; i++{
- z = z - (z*z-x)/(2*z)
- }
- return z
- }
- func main() {
- fmt.Println(Sqrt(3))
- }
1). 使用的牛顿迭代法,迭代公式 z' = z - (z^2-x) / (2z);
- 练习——求复数立方根:
- package main
- import (
- "fmt"
- "math/cmplx"
- )
- func Cbrt(x complex128) complex128 {
- z := complex128(1.0)
- for i:= 0; i<20; i++{
- z = z - (z*z*z-x)/(3*z*z)
- }
- return z
- }
- func main() {
- fmt.Println(Cbrt(2i))
- fmt.Println(cmplx.Pow(2i,1.0/3))
- }
1). 还是使用的牛顿迭代法,迭代公式 z' = z - (z^3-x) / (3z^2); 2). 这个题目也能让让人熟悉一下 go 内置的复数运算;math/cmplx 包有 Pow 有幂指数函数;
- 练习——求斐波那契数列:
- package main
- import "fmt"
- // fibonacci is a function that returns
- // a function that returns an int.
- func fibonacci() func() int {
- pre, next := 0,1
- return func()int{
- pre, next = next, pre+next
- return pre
- }
- }
- func main() {
- f := fibonacci()
- for i := 0; i < 10; i++ {
- fmt.Println(f())
- }
- }
1). 闭包求 Fibonacci 数列; 2). 基本就是采用了官网的方法,只不过当初自己写的时候,并没有用 pre,next = next, pre+next 这种写法,而是通过一个辅助变量实现;
- 练习——使用 map 统计字数:
- package main
- import (
- "tour/wc"
- "strings"
- )
- func WordCount(s string) map[string]int {
- words := strings.Fields(s) // split words
- ans := make(map[string] int)
- for _,word := range words{ // get one word
- _,ok := ans[word] // exist ?
- if !ok {
- ans[word] = 1
- }else{
- ans[word]++
- }
- }
- return ans
- }
- func main() {
- wc.Test(WordCount)
- }
1). 只是 go tour 的一个练习,在本地貌似直接跑的话跑不通,使用的 tour/wc 测试程序貌似是 google 自己的,所以写的这个程序只能拷到这里试了。 2). 统计字数思路倒也简单,先将长句子分割成一个一个单词,然后用个 map 统计;一个一个滴读单词,如果存在那么数量 +1;如果不存在,那数量就是 1。 3). 写这个程序,e 体会到了map 变量使用前必须 make 一下,否则不能使用;
由于篇幅所限,也为了看着方便,前面的例子见上一篇文章,后面的例子见下一篇文章。
有疑问加站长微信联系(非本文作者)