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()//给管理员发送邮件
    
    }
    
    
  • 相关阅读:
    事务传播机制,搞懂。
    洛谷 P1553 数字反转(升级版) 题解
    洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here 题解
    洛谷 P1055 ISBN号码 题解
    洛谷 P2141 珠心算测验 题解
    洛谷 P1047 校门外的树 题解
    洛谷 P1980 计数问题 题解
    洛谷 P1008 三连击 题解
    HDU 1013 题解
    HDU 1012 题解
  • 原文地址:https://www.cnblogs.com/hanajiang/p/13774230.html
Copyright © 2011-2022 走看看