zoukankan      html  css  js  c++  java
  • Golang语言学习笔记(六)

    Golang函数详解

    1.函数定义

    函数是组织好的、可重复使用的、用于执行指定任务的代码块。
    Go语言中支持:函数、匿名函数和闭包
    Go 语言中定义函数使用func关键字,具体格式:
    func 函数名(参数)(返回值){
    函数体
    }
    其中:
    函数名:由字母、数字、下划线组成。但是函数名的第一个字母不能是数字。在同一个包内,函数名也不能重复
    参数:参数由参数变量和参数变量的类型组成,多个参数之间用,分割
    返回值:返回值由返回值变量和其变量的类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分割
    函数体:实现指定功能的代码块

    2.函数参数

    参数类型简写
    可变参数

    3.函数返回值

    返回值命名:函数定义时可以给返回值命名,变更在函数体中直接使用这些变量,最后通过return关键字返回

    4.函数变量作用域

    全局变量
    全局变量是定义在函数外部的变量,它在整个运行周期内都有效。在函数中可以访问到全局变量。
    局部变量
    局部变量是函数内部定义的变量,函数内定义的变量无法在该函数外使用

    5.函数类型与变量

    定义函数类型

    可以使用type关键字来定义一个函数类型,具体格式如下:

    type calculation func(int,int) int

    上面语句定义了一个calculation类型,它是一种该函数类型,这种函数接收两个int类型的参数并且返回一个int类型的返回值。
    简单来说,凡是满足这个条件的函数都是calculation类型的函数,例如下面的add和sub是calculation类型。

    func add(x, y int) int {
        return x + y
    }
    
    func sub(x, y int) int {
        return x - y
    }
    

    函数可以作为另一个函数的参数

    函数可以作为返回值

    6.匿名函数和闭包

    匿名函数
    函数可以作为返回值,但是在Go语言中函数内部不能再像之前那样定义函数了,只能匿名定义函数。匿名函数就是没有函数名的函数,匿名函数的定义格式如下:
    func (参数)(返回值){
    函数体
    }
    匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数
    闭包
    闭包可以理解成"定义在一个函数内部的函数"。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说是函数和其引用环境的组合体。

    7.函数的递归调用

    函数调用自己本身

    8.defer语句

    Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按derfer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。
    defer执行时机

    在Go语言的函数中 return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的实际就在返回值赋值操作后,RET指令执行前。

    9.内置函数panic/recover

    内置函数 介绍
    close 主要用来关闭channel
    len 用来求长度,比如string、array、slice、map、channel
    new 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
    make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
    append 用来追加元素到数组、slice中
    panic和recover 用来做错误处理

    Go语言中目前(Go1.12)没有异常机制,但是使用panic/recover模式来处理错误。panic可以在任何地方引发,但是recover只有在defer调用的函数中有效。

    代码:

    package main
    
    import (
    	"fmt"
    	"sort"
    	"errors"
    )
    
    //求两个数的和
    func sum(a int, b int) int { //返回值只有一个的时候,可以不加()
    	sum := a + b
    	return sum
    }
    
    //求两个数的差
    func sub(a, b int) int { //函数参数的简写,当两个参数类型一致时,可以省略前边参数类型的声明,保留最后一个即可
    	sub := a - b
    	return sub
    }
    
    //函数的可变参数,可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...来标识
    func sumFn(x ...int) int {
    	// fmt.Printf("%v--%T",x,x)//[11 22 33 44 55]--[]int
    	sum := 0
    	for _, v := range x {
    		sum += v
    	}
    	return sum
    }
    
    //固定参数和可变参数搭配使用
    func sumFn2(x int, y ...int) int {
    	fmt.Println(x, y) //100 [2 3 4 5]
    	sum := x
    	for _, v := range y {
    		sum += v
    	}
    	return sum
    
    }
    
    //return关键字一次可以返回多个值
    func calc(x, y int) (int, int) {
    	sum := x + y
    	sub := x - y
    	return sum, sub
    }
    
    //返回值命名:函数定义时可以给返回值命名,并在函数体中直接使用给这些变量,最后通过return关键字 返回
    func calc1(x, y int) (sum, sub int) { //返回值参数类型也可以简写
    	sum = x + y
    	sub = x - y
    	return
    }
    
    //函数可以没有参数,没有返回值
    func test() {
    	fmt.Println("test")
    }
    
    //切片升序排序
    func sortIntAsc(slice []int) []int {
    
    	for i := 0; i < len(slice); i++ {
    		for j := i + 1; j < len(slice); j++ {
    			if slice[i] > slice[j] {
    				tmp := slice[i]
    				slice[i] = slice[j]
    				slice[j] = tmp
    			}
    		}
    	}
    	return slice
    }
    
    //切片升序排序
    func sortIntDesc(slice []int) []int {
    
    	for i := 0; i < len(slice); i++ {
    		for j := i + 1; j < len(slice); j++ {
    			if slice[i] < slice[j] {
    				tmp := slice[i]
    				slice[i] = slice[j]
    				slice[j] = tmp
    			}
    		}
    	}
    	return slice
    }
    
    func mapSort(m1 map[string]string) string {
    	var sliceKey []string
    	for k, _ := range m1 {
    		sliceKey = append(sliceKey, k)
    	}
    
    	sort.Strings(sliceKey)
    
    	var str string
    	for _, v := range sliceKey {
    		str += fmt.Sprintf("%v=>%v ", v, m1[v])
    	}
    
    	return str
    
    }
    
    var a = "全局变量"
    
    func run() {
    
    	var b = "局部变量"
    	fmt.Println("run方法a=", a)
    	fmt.Println("run方法b=", b)
    	fmt.Println("run")
    }
    
    //自定义一个函数类型
    type calculate func(int, int) int //表示定义一个calculate类型
    func add(x, y int) int {
    	return x + y
    }
    
    func sub1(x, y int) int {
    	return x - y
    }
    
    type myInt int
    
    //函数作为另一个函数的参数
    func add2(x, y int) int {
    	return x + y
    }
    
    func sub2(x, y int) int {
    	return x - y
    }
    
    func calc2(x, y int, cb calculate) int {
    
    	return cb(x, y)
    }
    
    //函数作为返回值
    func add3(x, y int) int {
    	return x + y
    }
    
    func sub3(x, y int) int {
    	return x - y
    }
    
    func do(o string) calculate {
    	switch o {
    	case "+":
    		return add3
    	case "-":
    		return sub3
    	case "*":
    		return func(x, y int) int {
    			return x * y
    		}
    	default:
    		return nil
    	}
    }
    
    func fn1() {
    	fmt.Println("方法1")
    }
    
    func fn2() {
    	fn1()
    	fmt.Println("方法2")
    }
    
    //传入1个整数,递归打印出1到这个数之内的所有整数
    func func1(n int) {
    	if n > 0 {
    		fmt.Println(n)
    		n--
    		func1(n)
    	}
    }
    
    //使用递归实现1-100的和
    func func2(n int) int {
    	if n > 1 {
    		return n + func2(n-1)
    	} else {
    		return 1
    	}
    
    }
    
    //闭包
    //闭包的写法:函数里面嵌套一个函数,最后返回里面的函数
    func adder1() func() int {
    	var i = 10
    	return func() int {
    		return i + 1
    	}
    }
    
    func adder2() func(y int) int {
    	var i = 10 //常驻内存,不污染全局
    	return func(y int) int {
    		i += y
    		return i
    	}
    }
    
    func f1() {
    	fmt.Println("开始")
    
    	defer func() {
    		fmt.Println("aaaa")
    		fmt.Println("bbbb")
    	}()
    	fmt.Println("结束")
    }
    
    //调用这个方法返回值为0
    func f2() int {
    	var a int
    	defer func() {
    		a++
    	}()
    
    	return a
    }
    
    func f3() (a int) {
    	defer func() {
    		a++
    	}()
    
    	return a
    }
    
    
    //
    func return1() int{
    	x :=5
    	defer func() {
    		x++
    	}()
    	return x//5
    }
    
    func return2() (x int){
    	defer func() {
    		x++
    	}()
    	return 5 //6
    }
    
    func return3() (y int){
    	x := 5
    	defer func() {
    		x++
    	}()
    	return x//5
    }
    
    func return4() (x int){
    	defer func(x int) {
    		x++
    	}(x)//这里的参数x传的是0,defer注册要延迟执行的函数时该函数所有的参数都需要确定其值
    	return 5//5
    }
    
    func calc3(index string,a,b int) int {
    	ret := a + b
    	fmt.Println(index,a,b,ret)
    	return ret
    }
    
    
    //panic/recover
    func funcA(){
    	fmt.Println("funcA")
    }
    
    func funcB(){
    	defer func(){
    		err := recover()
    		if err != nil{
    			fmt.Println("err:",err)
    		}
    	}()
    	panic("抛出一个异常")
    }
    
    func funcC(a, b int) int {
    	return a/b
    }
    
    func funcD(a, b int) int {
    	defer func(){
    		err := recover()
    		if err != nil {
    			fmt.Println("error",err)//error runtime error: integer divide by zero
    		}
    	}()
    	return a/b
    }
    
    func readFile(fileName string) error {
    	if fileName == "man.go" {
    		return nil
    	} else {
    		return errors.New("读取文件失败")
    	}
    }
    
    func myFn(){
    	defer func(){
    		e :=recover()
    		if e != nil{
    			fmt.Println("给管理员发送邮件")
    		}
    	}()
    	err := readFile("xxx.go")
    	if err != nil{
    		panic(err)
    	}
    }
    
    func main() {
    	// sum1 := sum(1,2)
    	// fmt.Println(sum1)//3
    
    	// sum2 := sum(23,45)
    	// fmt.Println(sum2)//68
    
    	// sub1 := sub(23,12)
    	// fmt.Println(sub1)//11
    
    	// sum3 := sumFn(11,22,33,44,55)
    	// fmt.Print(sum3)//165
    
    	// sum4 := sumFn2(100, 2, 3, 4, 5)
    	// fmt.Println(sum4) //114
    
    	// sum5, sub2 := calc(10, 2)
    	// fmt.Println(sum5, sub2)//12 8
    
    	// sum6, sub3 := calc(10, 2)
    	// fmt.Println(sum6, sub3) //12 8
    
    	// a, _ := calc1(10, 2)
    	// fmt.Println(a) //12
    
    	// test()
    
    	//把之前提到过的选择排序封装成方法,实现整形切片的升序降序排序
    	// var sliceA = []int{12,34,37,35,556,36,2}
    	// sortSlice := sortIntAsc(sliceA)
    	// fmt.Println(sortSlice)//[2 12 34 35 36 37 556]
    
    	// sortSliceDesc := sortIntDesc(sliceA)
    	// fmt.Println(sortSliceDesc)//[556 37 36 35 34 12 2]
    
    	/*
    		m1 := map[string]string{
    			"username": "zhangsan",
    			"age":      "20",
    			"sex":      "Male",
    			"height":   "180",
    		}
    		输出:age=>20 height=>180 sex=>Male username=>zhangsan
    
    		将map对象m1,封装一个方法,要求按照key升序排序,左后输出key=>value
    	*/
    
    	// m1 := map[string]string{
    	// 	"username": "zhangsan",
    	// 	"age":      "20",
    	// 	"sex":      "Male",
    	// 	"height":   "180",
    	// }
    	// str := mapSort(m1)
    	// fmt.Println(str)//age=>20 height=>180 sex=>Male username=>zhangsan
    
    	//全局变量和局部变量
    	// fmt.Println("main方法a=",a)
    	// // fmt.Println("main方法b=",b)//undefined: b
    	// run()
    	/*
    		main方法a= 全局变量
    		run方法a= 全局变量
    		run方法b= 局部变量
    		run
    	*/
    
    	//i就是局部变量 只能在for方法体内使用
    	// for i := 0; i < 10; i++{
    	// 	fmt.Println(i)
    	// }
    	// //fmt.Println(i)//undefined: i
    
    	//在本作用域相当于全局变量
    	// var flag = true
    	// if flag {
    	// 	fmt.Println(flag)//true
    	// }
    	// fmt.Println(flag)//true
    
    	//flag块作用域 局部变量
    	// if flag := true; flag {
    	// 	fmt.Println(flag)//true
    	// }
    	// // fmt.Println(flag)//undefined: flag
    
    	// var c calculate
    	// c = add
    	// c = test//cannot use test (type func()) as type calculate in assignment
    	// fmt.Printf("c的类型:%T",c)//c的类型:main.calculate
    
    	// fmt.Println(c(10,5))//15
    
    	// f := sub1
    	// // fmt.Printf("f的类型:%T",f)//f的类型:func(int, int) int
    	// fmt.Println(f(10,5))//5
    
    	// var a int = 10
    	// var b myInt = 20
    	// // fmt.Printf("a的类型:%T
    ",a)//a的类型:int
    	// // fmt.Printf("b的类型:%T
    ",b)//b的类型:main.myInt
    
    	// // fmt.Println(a + b)//invalid operation: a + b (mismatched types int and myInt)
    	// fmt.Println(a + int(b))//30
    
    	// sum := calc2(10, 5, add2)
    	// fmt.Println(sum) //15
    
    	// sub := calc2(10, 5, sub2)
    	// fmt.Println(sub) //5
    
    	//匿名函数
    	// mul := calc2(3, 4, func(x, y int) int {
    	// 	return x * y
    	// })
    
    	// fmt.Println(mul) //12
    
    	//函数作为返回值
    	// var a = do("+")//用a接收返回的函数
    	// fmt.Printf("a的类型是:%T
    ",a)//a的类型是:main.calculate
    	// fmt.Println(a(1,2))//3
    
    	// var b = do("*")
    	// fmt.Printf("b的类型是:%T
    ",b)//a的类型是:main.calculate
    	// fmt.Println(b(1,2))//2
    
    	//匿名函数
    	// func() {
    	// 	fmt.Println("test...")
    	// }//func literal evaluated but not used
    
    	// func() {
    	// 	fmt.Println("test...")
    	// }()//将函数作为一个整体,加上()执行,匿名自执行函数
    
    	// var fn = func (x, y int) int {
    	// 	return x * y
    	// }
    	// fmt.Println(fn(2,4))//8
    
    	//匿名自执行函数接收函数
    	// func(x, y int) {
    	// 	fmt.Println(x + y)//30
    	// }(10, 20)
    
    	//函数的递归调用
    	// fn2()//可以在一个方法中调用另一个方法
    	//也可以在一个方法中,调用本身
    
    	//1.for循环实现1到100的和
    	//使用for循环实现
    	// var sum = 0;
    	// for i := 1; i <= 100; i++ {
    	// 	sum += i
    	// }
    	// fmt.Println(sum)//5050
    
    	//使用递归实现1-100的和
    	// fmt.Println(func2(100))
    
    	//2.传入1个整数,递归打印出1到这个数之内的所有整数
    	// func1(10)
    
    	/*
    		全局变量特点:
    			1.常驻内存
    			2.污染全局
    
    		局部变量的特点:
    			1.不常驻内存
    			2.不污染全局
    
    		闭包:
    			1.可以让一个变量常驻内存
    			2.可以让一个变量不污染全局
    	*/
    
    	/*
    		闭包
    
    			1.闭包是指有权访问另一个函数作用域中的变量的函数。
    			2.创建闭包的常见的方式就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量
    
    			注意:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。
    			过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包
    	*/
    
    	// var a = adder1()//表示执行方法
    	// fmt.Println(a())//11
    	// fmt.Println(a())//11
    
    	// var b = adder2()
    	// fmt.Println(b(10))//20
    	// fmt.Println(b(10))//30
    	// fmt.Println(b(10))//40
    	//adder2中的i对闭包来说是全局变量
    
    	//defer语句
    	//1.defer的使用演示
    	// fmt.Println("开始")
    	// defer fmt.Println(1)
    	// defer fmt.Println(2)
    	// defer fmt.Println(3)
    	// fmt.Println("结束")
    	/*
    		输出:
    		开始
    		结束
    		3
    		2
    		1
    	*/
    	//2.defer在命名返回值和匿名返回函数中表现不一样
    	// f1()
    	/*
    		输出:
    		开始
    		结束
    		aaaa
    		bbbb
    	*/
    
    	// fmt.Println(f2())//0
    	// fmt.Println(f3())//1
    
    	// fmt.Println(return1())//5
    	// fmt.Println(return2())//6
    	// fmt.Println(return3())//5
    	// fmt.Println(return4())//5
    
    
    	//defer注册要延迟执行的函数时该函数所有的参数都需要确定其值
    	// x := 1
    	// y := 2
    	// defer calc3("AA",x,calc3("A",x,y))
    	// x = 10
    	// defer calc3("BB",x,calc3("B",x,y))
    
    	/*
    		注册顺序
    			1.defer calc3("AA",x,calc3("A",x,y))
    			2.defer calc3("BB",x,calc3("B",x,y))
    		执行顺序:
    			1.defer calc3("BB",x,calc3("B",x,y))
    			2.defer calc3("AA",x,calc3("A",x,y))
    
    		--------------------------
    		1.calc3("A",x,y) A 1 2 3
    		2.calc3("B",x,y) B 10 2 12
    		3.calc3("BB",x,calc3("B",x,y)) BB 10 12 22
    		4.calc3("AA",x,calc3("A",x,y)) AA 1  3 4
    	*/
    
    	// funcA()
    	// funcB()
    	// fmt.Println("结束")
    
    	// fmt.Println(funcC(10,0))//当除数为0并且在方法中没有处理这个异常时,报错:panic: runtime error: integer divide by zero
    	// fmt.Println(funcD(10,0))//使用recover处理异常,程序不会崩溃,后面的代码可以正常执行
    
    
    	myFn()//给管理员发送邮件
    
    }
    
    
  • 相关阅读:
    一个简单实现的string类
    Python基础(二)
    Python基础(一)
    区块链初探
    某电商平台开发记要——客服系统
    某电商平台开发记要
    Upload files to aliyunOSS with bootstrap-fileinput
    jquery.validate[.unobtrusive]和Bootstrap实现tooltip错误提示
    PostgreSQL笔记
    天冷了,那些树还好吗?
  • 原文地址:https://www.cnblogs.com/hanajiang/p/13774230.html
Copyright © 2011-2022 走看看