Python 测开27期 - 柒柒 - Golang笔记

导包

// 分组导包
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 声明
  • 常量
    • const 参数名

数据类型

基本类型

  • bool
  • string
  • int int8 int16 int32 int64
    • rune:int32 的别名,表示一个 Unicode 码点
  • uint uint8 uint16 uint32 uint64 uintptr
    • byte:uint8 的别名
  • float32 float64
  • complex64 complex128

结构体

  • 与 python 的数据类相似
  • 语法
// 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() 创建切片
// 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")
}