作者:edelweiss 日期:2020年5月14日
参考资料 Go指南 http://tour.studygolang.com/
包、变量和函数
Go 语言参数类型位置在后
Go 语言的参数类型放在函数形参的后面,这点和C语言是相反的。
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
Go 语言参数类型简化写法
Go 语言参数类型相同的情况下可以只在最后一个参数后面声明一次。
func add(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
Go语言支持返回多个参数
值得注意的是,其返回值列表书写在函数的后面,后面跟着的是形参列表。
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
Go语言的返回值可像一个变量一样使用
在返回的时候,只需要单独写一个return。
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
var 关键字用来定义变量,名称在前类型在后。
var c, python, java bool
func main() {
var i int
fmt.Println(i, c, python, java)
}
Go 语言在定义初始化在一条语句中的时候,可以自动推导类型。
var i, j int = 1, 2
func main() {
var c, python, java = true, false, "no!"
fmt.Println(i, j, c, python, java)
}
Go 语言更多使用 := 的语法声明变量
这种语法必须在函数体内使用
func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, "no!"
fmt.Println(i, j, k, c, python, java)
}
Go 语言的基本数据类型 TODO
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名
// 代表一个Unicode码
float32 float64
complex64 complex128
变量在定义后没有初始化是零值
零值是:
数值类型为0
,
布尔类型为false
,
字符串为""
(空字符串)。
Go 语言要求显示的类型转换
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z int = int(f)
fmt.Println(x, y, z)
}
常量的定义
定义常量用 const 关键字,上面的 := 语法定义不了常量。
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
const Truth = true
fmt.Println("Go rules?", Truth)
}
数值型的常量根据上下文确定类型大小
const (
Big = 1 << 100
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
流程控制语句
for 循环
Go 中的 for 循环 ( ) 被拿掉了
const (
Big = 1 << 100
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
for 把while 的工作也作了
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
死循环
func main() {
for {
}
}
if 括号也没啦
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
还有这种操作?
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// 这里开始就不能使用 v 了
return lim
}
Go 语言中switch语句自带break
如果想要传递给下一个case块,使用fallthrough。
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)
}
}
没有条件的switch 相当于switch true
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
defer语句将会延迟到上层函数返回后执行
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
defer 栈
延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
复杂类型 struct slice 和 map
指针
Go 具有指针。指针保存了变量的内存地址。
类型 *T 是指向类型T的值的指针。
var p *int
&符号会生成一个指向其作用对象的指针
i := 42
p = &i
*符号表示指针指向下一层的值
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
和C语言不同,Go没有指针运算。
结构体
结构体指针
结构体字段可以通过结构体指针来访问
结构体文法
类似于用一个匿名对象给一个新对象赋值?
使用 字段名: 语法可以仅列出部分字段。
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // 类型为 Vertex
v2 = Vertex{X: 1} // Y:0 被省略
v3 = Vertex{} // X:0 和 Y:0
p = &Vertex{1, 2} // 类型为 *Vertex
)
func main() {
fmt.Println(v1, p, v2, v3)
}
数组
类型[n]T 是一个有 n 个类型为 T 的值的数组,数组的长度是其类型的一部分,因此数组不能改变大小。
slice
一个slice 会指向一个序列的值,并且包含了长度信息。
[]T 是一个元素类型为 T 的 slice。
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])
}
}
对 slice 切片
slice 可以重新切片,创建一个新的 slice 值指向相同的数组。
语法 slice[首:尾后迭代器]
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])
// 省略下标代表从 0 开始
fmt.Println("p[:3] ==", p[:3])
// 省略上标代表到 len(s) 结束
fmt.Println("p[4:] ==", p[4:])
}
构造 slice
slice 由函数 make 创建
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
nil slice
slice 的零值是 'nil'
package main
import "fmt"
func main() {
var z []int
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}
}
向 slice 添加元素
Go 提供了一个内建函数 func append(s []T目标, vs元素 ...T) []T
package main
import "fmt"
func main() {
var a []int
printSlice("a", a)
// append works on nil slices.
a = append(a, 0)
printSlice("a", a)
// the slice grows as needed.
a = append(a, 1)
printSlice("a", a)
// we can add more than one element at a time.
a = append(a, 2, 3, 4)
printSlice("a", a)
}
append 的第一个参数 s 是一个类型为 T 的数组,其余类型为T 的值将会添加到 slice。
append 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice.
如果 s 的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。返回的slice 会指向这个新分配的数组。
range 范围 for 循环
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)
}
}
range 可通过_忽略参数
map
map 在使用之前必须用 make 而不是 new 来创建;值为 nil 的 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,
}
fmt.Println(m["Bell Labs"])
}
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)
}
写多个map键值对的时候可以省略值的类型
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)
}
修改map
在map中插入或者修改一个元素:m[key] = elem
获得元素:elem = m[key]
删除元素: delete(m, key)
检查某个键值是否存在 elem, ok = m[key] 如果这个值不存在,elem = nil值,ok = false
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)
}
函数的闭包
闭包返回一个函数,且此函数跟函数体内部的变量绑定,(只初始化一次)。
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),
)
}
}
#结果:
0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90
方法和接口
方法
Go 没有类。然而,仍然可以在结构体类型上定义方法。方法接收者出现在啊 func 关键字和方法名之间的参数中。
package main
import(
"fmt"
)
type Vertex struct {
X, Y float64
}
func(v *Vertex) Sum() float64{
return (v.X + v.Y)
}
func main(){
v := &Vertex{3,4}
fmt.Println(v.Sum())
}
接口
接口类型是由一组方法定义的集合,接口类型的变量可以存放实现接口方法的类型的对象。
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 实现了 Abser
a = &v // a *Vertex 实现了 Abser
// 下面一行,v 是一个 Vertex(而不是 *Vertex)
// 所以没有实现 Abser。
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
隐式接口
//TODO