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)
}

函数参数与返回值

可变长参数定义:

  1. 写在末尾
  2. 定义一个
  3. 用三个点加类型
  4. 传入的是一个切片

传递:

  1. 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接口的初始化方法:

  1. 通过errors包的New方法创建
  2. 通过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("指令错误")
		}
	}
}
posted @ 2022-04-30 15:23  元气少女郭德纲!!  阅读(45)  评论(0编辑  收藏  举报