导包
// 分组导包
import (
"fmt"
"math"
)
导出名
- 在导入一个包时,只能引用其中已导出的名字
- 以大写字母开头的函数、变量等,代表已导出,可在包外使用
函数
- 函数可以没有参数或接受多个参数
- 注意类型在变量名之后
- 当连续多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略
/* func 函数名(参数1 类型,参数2 类型)(返回值1类型,返回值2类型) {
return xxx
}*/
func add(x int, y int, z string) (int, string) {
fmt.Println(z)
school := "horgwarts"
sum := x + y
return sum, school
}
// 类型缩写
func add(x, y int, z string) int {
fmt.Println(z)
return x + y
}
// 命名返回值
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
函数值用作函数的参数或返回值
func get_state(age int) string {
var state string
if age < 18 {
state = "young"
} else if age < 30 {
state = "strong"
} else {
state = "old"
}
return state
}
// 函数作为参数
// 语法: func 函数名(参数名 func(函数参数类型1,函数参数类型2...)函数返回类型)函数返回类型 {}
func say_state(fn func(int) string, age int) {
fmt.Printf("哇,这个人非常 %v", fn(age))
}
func main() {
say_state(get_state, 8)
}
闭包
练习:斐波纳契闭包
// 返回一个“返回int的函数”
func fibonacci() func(int) int {
num := []int{0}
return func(x int) int {
// 斐波纳契数列:F(0)=0,F(1)=1,F(n) = F(n-1) + F(n-2)
switch x {
case 0:
return num[x]
case 1:
num = append(num, 1)
return num[x]
default:
sum := num[x-2] + num[x-1]
num = append(num, sum)
return num[x]
}
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f(i))
}
}
声明
- 变量
- var 语句可以出现在包或函数级别
- 在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明
- 常量
数据类型
基本类型
- bool
- string
- int int8 int16 int32 int64
- rune:int32 的别名,表示一个 Unicode 码点
- uint uint8 uint16 uint32 uint64 uintptr
- float32 float64
- complex64 complex128
结构体
// type 结构体名称 struct {变量 变量类型}
type Student struct {
name string
age int
sex string
}
// 创建结构体
// 若只录入部分字段,则其他默认为零值
stu_01 := Student{"kevin", 18, "male"}
stu_02 := Student{name:"kevin", age:18, sex:"male"}
// 引用结构体属性
stu_01.name
方法
值接收器(value receiver)
// Student 创建 Student 结构体
type Student struct {
name string
no string
}
// 方法只是个带接收者参数的函数
// 语法:func (结构体对象 结构体名称) 方法名(参数) 返回值类型 { 语句 }
func (stu Student) info() {
fmt.Printf("姓名:%v ,学号:%v \n", stu.name, stu.no)
}
// 使用 Student{name,no}.info() 调用
// 未绑定结构体
func logg(stu Student) {
fmt.Printf("姓名:%v ,学号:%v \n", stu.name, stu.no)
}
// 使用 logg(Student{name,no}) 调用
type MyString string
func (s MyString) printtt() {
fmt.Println(s + s)
}
func main() {
s := MyString("kevin")
s.printtt()
fmt.Println(s)
}
指针接收器(pointer receiver)
- 值接收器和指针接收器的区别
- 区别1:修改原始值 vs. 修改指针指向的值
- 值接收器方法中修改的字段不会影响原来的值
- 指针接收器对值的修改是永久性的,会影响原来的值
- 区别2:复制对象 vs. 传递引用
- 值接收器方法是将整个对象的拷贝传递给方法内部
- 指针接收器通过传递对象的指针,使得方法内部对对象的修改可以影响原始对象
type Demo struct {
x float64
y float64
}
// 不带*号,代表值接收器
func (d Demo) plus() float64 {
return d.x + d.y
}
// 带*号,代表指针接收器
func (d *Demo) edit(f float64) {
d.x = d.x * f
d.y = d.y * f
}
func main() {
s := Demo{3, 4}
fmt.Printf("S的值为:%v \n", s)
fmt.Printf("相加值为:%v \n", s.plus())
s.edit(10)
fmt.Printf("修改后S的值为:%v \n", s)
fmt.Printf("修改后相加值为:%v \n", s.plus())
}
数组
// 拥有 10 个整数的数组
// 声明关键字 数组名 [长度]数组类型
var a [10]int
// 定义数组长度
b := [6]int{1, 2, 3, 4} // [1 2 3 4 0 0]
// 不定义数组长度
b := []int{1, 2, 3, 4} // [1 2 3 4]
// 数组嵌套
// 第一个[]代表外部数组,[]int代表数组内嵌套类型为整数的数组
s := [][]int{
{1, 2, 3},
{4, 5, 6},
}
切片
- 切片并不存储任何数据,更改切片的元素会修改其底层数组中对应的元素
// 切片等同于没有长度的数组
b := []int{1, 2, 3, 4, 5, 6}
fmt.Println(b)
// 切片
s1 := b[1:3]
fmt.Println(s1)
// 修改切片的元素
s1[0] = 77
fmt.Println(s1)
fmt.Println(b)
// 创建结构体切片
var stulist = []Student{
{name: "a", age: 1, sex: "male"},
{name: "b", age: 2, sex: "male"},
}
- 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数
s_list := []int{1, 2, 3, 4, 5}
s := s_list[:4]
// 切片为:[1 2 3 4] ,长度:4 ,容量:5
s = s[1:] // 此时,s的底层数组是第一次的切片,并非s_list
// 切片为:[2 3 4] ,长度:3 ,容量:4
// make([]类型,长度,容量)
a := make([]Student, 5, 5)
// 打印切片详情
func get_info(s []int) {
fmt.Printf("切片为:%v ,长度:%d ,容量:%d \n", s, len(s), cap(s))
}
func main() {
var s []int
get_info(s)
// 添加空切片
s = append(s, 0)
get_info(s)
// 切片容量按需增长
s = append(s, 1, 2, 3)
get_info(s)
}
for 循环
// for 关键字后的条件无需在()小括号内
func loop_01() {
for i := 0; i < 10; i++ {
if i == 5 {
continue
}
s := fmt.Sprintf("i的值为:%d", i)
fmt.Println(s)
}
}
// 等同于 while
func loop_02() {
i := 0
for i < 6 {
if i == 3 {
break
}
s := fmt.Sprintf("i的值为:%d", i)
fmt.Println(s)
i++
}
}
range 关键字
- for 参数 := range 可遍历对象 { 语句 }
func main() {
var s = []int{11, 22, 33, 44, 55}
// 返回列表的索引和值
for i, v := range s {
fmt.Printf("索引为:%v 值为:%v \n", i, v)
}
// 只返回列表的值
for _, v := range s {
fmt.Printf("值为:%v \n", v)
}
}
switch 条件语句
// i与case中的值匹配,则执行对应分支的语句,并跳出switch
func change() {
switch i := 5; i {
case 1:
fmt.Println("1")
case 5:
fmt.Println("5")
case 8:
fmt.Println("8")
}
}
// 等同于if-elif-else
func change(i int) {
switch {
case i == 1:
fmt.Println("1")
case i == 5:
fmt.Println("5")
default:
fmt.Println("default")
}
}
指针
func main() {
i, j := 20, 77
p := &i
fmt.Printf("i为:%d , p为:%d\n", i, *p)
*p = 100
fmt.Printf("i为:%d , p为:%d\n", i, *p)
p = &j
fmt.Printf("j为:%d , p为:%d\n", j, *p)
i = 1
fmt.Printf("i为:%d , p为:%d\n", i, *p)
}
映射
- 类似于python字典
- 映射的零值为 nil 。nil 映射既没有键,也不能添加键。
- make 函数会返回给定类型的映射,并将其初始化备用
创建映射
// 语法:make(map[键类型]值类型)
func main() {
// 初始化键类型和值类型
m := make(map[string]string)
// 新增键值对
m["kevin"] = "leader"
}
type Student struct {
name string
age int
}
// 语法:map[键类型]值类型 {}
func main() {
// 先声明结构体,再初始化map
m := map[string]Student{
"no1": {"kevin", 18},
}
// 直接初始化结构体的map
s := map[string]struct {
no int
score int
}{
"kevin": {1, 100},
}
fmt.Println(s)
}
修改映射
// 新增或修改元素
m[key] = value
// 删除元素
delete(m,key)
双赋值检查某个键是否存在
- 若 key 在 m 中,ok 为 true ;否则,ok 为 false
- 若 key 不在映射中,那么 elem 是该映射元素类型的零值
func main() {
m := map[string]int{}
v, ok := m["a"]
fmt.Printf("值为:%v , 当前: %v \n", v, ok)
m["a"] = 88
v, ok = m["a"]
fmt.Printf("值为:%v , 当前: %v \n", v, ok)
}
练习:统计词频
func WordCount(s string) map[string]int {
// 分割字符串
wordList := strings.Fields(s)
wordMap := make(map[string]int)
// 若key不在映射中,则值为0;所以每次加1
for _, word := range wordList {
wordMap[word] += 1
}
fmt.Println(wordMap)
return wordMap
}
func main() {
//wc.Test(WordCount)
WordCount("hello world hello shanghai")
}