Go函数
函数
函数定义与调用
// 定义(无参,无返回值)
func sayHelloworld() {
fmt.Println("hello world")
}
// 定义有参函数(形参)
func sayHi(name string) {
fmt.Println("你好:", name)
}
// 定义有参函数+返回值
func add(a int, b int) int {
return a + b
}
func main() {
sayHelloworld() // hello world
fmt.Printf("%T", sayHelloworld) // func()
sayHi("tcy") // 实参
rt := add(1, 5)
fmt.Println(rt)
}
函数参数与返回值
可变长参数定义:
- 写在末尾
- 定义一个
- 用三个点加类型
- 传入的是一个切片
传递:
- args...传递,解包
- 函数参数
func add(a, b int) int { // ab都是int类型,可以简写
return a + b
}
func addN(a, b int, args ...int) int {
fmt.Printf("%T\n", args) // args的接收以切片输出
total := a + b
for _, v := range args { // 实现传入参数与args结果相加
total += v
}
return total
}
func cacl(op string, a, b int, args ...int) int {
switch op {
case "addN":
return addN(a, b, args...) // 函数内调用函数传参,args...为解包,防止切片套切片
}
return -1
}
func main() {
fmt.Println(add(1, 2))
fmt.Println(addN(1, 8, 6, 5)) // 20
fmt.Println(cacl("addN", 1, 2, 3, 4)) //10
args := []int{1, 9, 3}
fmt.Println(cacl("addN", 1, 7, args...)) // 21 将args获取切片值解包,传入函数
}
- 解包
nums := []int{1, 2, 3, 4, 5}
nums = append(nums[:1], nums[2:]...) // nums[2:]...和args一样解包,切片变int
fmt.Println(nums) // [1 3 4 5]
- 返回值
import "fmt"
func calc(a, b int) (int, int, int, int) { //定义多个返回值
return a + b, a - b, a * b, a * b
}
func calc2(a, b int) (sum int, diff int, product int, merchant int) { // 命名返回值
sum = a + b
diff = a - b
product = a * b
merchant = a / b
return
}
func main() {
fmt.Println(calc(1, 2)) // 3 -1 2 2
fmt.Println(calc2(1, 2)) // 3 -1 2 2
}
递归
递归是指函数直接或间接调用自己,递归常用于解决分治问题,为相同小问题进行解决,需要关注终止条件。
公式(n的阶乘):
f(n)
f(n) = f(n - 1) + n
f(100) = f(99) + 100
// 示例一:
func addN(n int) int {
if n == 1 {
return 1
}
return n + addN(n-1) // 传入参数的阶乘
}
func main() {
fmt.Println(addN(4))
}
// 示例二:
func addN(n int) int {
if n == 0 {
return 1
} else if n < 0 {
return -1
}
return n * addN(n-1)
}
func main() {
fmt.Println(addN(4)) // 4 * 3 * 2 * 1
}
匿名
函数签名组成:
函数参数 数量,对应类型,函数的返回值
// 匿名函数
// 方式一“
sayHello := func(name string) {
fmt.Println("hello", name)
}
sayHello("kk") // hello kk
// 方式二:
func(name string) {
fmt.Println("hello", name)
}("tcy") // hello tcy
函数传递给函数
// 格式化,将传递的数据按照每行打印还是按照一行按|分隔打印
// 函数定义一个形参的函数类型,将函数通过实参进行传递
func print(callback func(...string), args ...string) {
fmt.Printf("print函数输出")
callback(args...)
}
func list(args ...string) {
for i, v := range args {
fmt.Println(i, ":", v)
}
}
func main() {
print(list, "A", "B", "C", "D")
// print函数输出0 : A
//1 : B
//2 : C
//3 : D
}
闭包
在函数体类引用父作用域的变量
func main() {
addBase := func(base int) func(int) int {
return func(n int) int {
return base + n
}
}
add2 := addBase(2)
fmt.Println(add2(3)) // 5
fmt.Println(addBase(2)(3)) // 5
}
值类型&引用类型
值类型:
用数组打比方,新建一个变量值为一个变量,当对新变量做改动,不会影响老的变量值,那么就是值类型
如:数组,int,bool,float,指针,结构体
引用类型:
用切片打比方,新建一个变量值为一个变量,当对新变量做改动,会影响到老变量的值,那么久是引用类型。
如:切片,映射,接口
值传递
在Go语言中参数传递默认均为值传递(形参为实参变量的副本),对于引用类型数据因其底层共享数据结构,所以在函数内可对引用类型数据修改从而影响函数外的原变量信息
func changeInt(a int) {
a = 100
}
func changeSlice(s []int) {
s[0] = 100
}
func main() {
num := 1
changeInt(num)
fmt.Println(num) // 1 因为int为值类型
nums := []int{1, 2, 3}
changeSlice(nums)
fmt.Println(nums) // 100 因为切片为引用类型
}
错误处理
无错误返回:nil
如何定义错误类型:error
error接口的初始化方法:
- 通过errors包的New方法创建
- 通过fmt.Errorf方法创建
// errors.New() 创建错误类型的值
func division(a, b int) (int, error) {
if b == 0 {
return -1, errors.New("disision by zero")
}
return a / b, nil
}
func main() {
fmt.Println(division(1, 3))
if v, err := division(10, 5); err == nil {
fmt.Println(v)
} else {
fmt.Println(err)
}
}
// fmt.Errorf
e := fmt.Errorf("Error: %s", "division by zero")
fmt.Printf("%T,%v\n", e, e) // *errors.errorString,Error: division by zero
go语言提供panic和recover函数用于处理运行时的错误,当调用panic抛出错误中断处理,有的流程控制常用于修复性错误,recover函数用于终止错误处理流程,
仅在defer(延迟函数)语句的函数中有效,用于截取错误处理流程,revover只能捕获到最后一个错误
- panic
尽量少用
func main() {
fmt.Println("main start")
panic("error") // 主动抛出错误
fmt.Println("over")
}
- recover
使用recover需要结合:defer & panic
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err) // panic抛出的错误,被延迟函数输出
}
}()
fmt.Println("main start")
panic("error") // 主动抛出错误
fmt.Println("over")
}
- 抓取程序错误,判断是否存在错误,有则返回
- recover必须写在defer之内
func test() (err error) {
defer func() {
if e := recover(); e != nil { // ② 被recover接受判断,赋值为e
err = fmt.Errorf("%v", e) // ③ 有值返回错误
}
}()
panic("error") // ① panic报出错误
return // ④ 没有值,说明没有错误
}
func main() {
err := test()
fmt.Println(err)
}
延迟函数
func main() {
// 匿名函数前加defer为延迟执行,在退出前执行
// 当存在多个延迟函数,先进后出,堆栈机制
defer func() {
fmt.Println("defer")
}()
defer func() {
fmt.Println("defer02")
}()
fmt.Println("main over") // main over defer
}
用户管理
用户信息存储
内存
用户结构:ID,名称,年龄,电话,地址
用户存储类型:映射
用户添加
用户修改
用户删除
用户查询
// 添加用户
func add(pk int, user map[string]map[string]string) {
var (
id string = fmt.Sprintf("%d", pk) // 将传入的形参从int改为string
name string
age string
tel string
addr string
)
fmt.Println(id)
fmt.Print("请输入姓名:")
fmt.Scan(&name)
fmt.Print("请输入年龄:")
fmt.Scan(&age)
fmt.Print("请输入电话:")
fmt.Scan(&tel)
fmt.Print("请输入家庭地址:")
fmt.Scan(&addr)
user[id] = map[string]string{
"id": id,
"name": name,
"age": age,
"tel": tel,
"addr": addr,
}
fmt.Println(name, age, tel, addr)
}
// 查询用户
// q为空,查找全部
//非空,必须名称,电话,地址,任意一个属性中包含q的内容显示
func query(user map[string]map[string]string) {
var q string
fmt.Print("请输入查询信息")
fmt.Scan(&q)
titel := fmt.Sprintf("%5s|%20s|%5s|%20s|%50s", "ID", "name", "age", "tel", "addr")
fmt.Println(titel) // 将转换为字符串的titel打印
fmt.Println(strings.Repeat("-", len(titel))) // 根据titel的长度打印出多少个-
for _, user := range user {
if q == "" || strings.Contains(user["name"], q) || strings.Contains(user["tel"], q) || strings.Contains(user["age"], q) || strings.Contains(user["addr"], q) {
fmt.Printf("%5s|%20s|%5s|%20s|%50s", user["id"], user["name"], user["age"], user["tel"], user["addr"])
fmt.Println()
}
}
}
func main() {
// 存储用户信息,key和value都为map
user := make(map[string]map[string]string)
id := 0
fmt.Println("欢迎使用TCY用户管理系统")
for {
var op string
fmt.Print(`
1. 新建用户
2. 修改用户
3. 删除用户
4. 查询用户
5. 退出
请输入指令:`)
fmt.Scan(&op)
fmt.Println("你输入的指令为:", op)
if op == "1" {
id++
add(id, user)
} else if op == "2" {
} else if op == "3" {
} else if op == "4" {
query(user)
} else if op == "5" {
break
} else {
fmt.Println("指令错误")
}
}
}