南山区住房和建设局网站官网中国疾控卫生应急服装
Go语言中的函数
go语言中函数特性
go语言有三种函数:普通函数、匿名函数(没有名称的函数)方法(定义在struct上的函数)。receiver
go语言中不运算函数重载(overload),也就是说不允许函数同名
go语言中的函数不能嵌套,但是可以嵌套匿名函数
函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数
函数可以作为参数传递给另一个函数
函数的返回值可以是另一个函数
函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数
函数参数可以没有名称
go语言中函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务,函数可以重复调用,从而达到代码的复用
func function_name([parameter_list])[return_types]{函数体
}

package mainimport "fmt"func sum(a int, b int) (res int) {res = a + breturn res
}
func main() {res := sum(1, 2)fmt.Printf("res: %v\n", res)
}
package mainimport "fmt"func compare(a int, b int) (max int) {if a > b {max = a} else {max = b}return max
}
func main() {res := compare(1, 2)fmt.Printf("res: %v\n", res)
}
go语言中的return
没有返回值
有一个返回值 (例如上面的求和以及比较两个数的大小的函数)
存在多个返回值,且在return中指定返回的内容
多个返回值,返回值名称没有被使用
return覆盖命名返回值,返回值名称没有被使用
package mainimport "fmt"func test() {fmt.Print("testing...")
}
func main() {test()
}
package mainimport "fmt"func test() (string, int) {name := "Y4y17"age := 24return name, age
}
func main() {name, age := test()fmt.Printf("name: %v\n", name)fmt.Printf("age: %v\n", age)
}
package mainimport "fmt"func test() (name string, age int) {name = "Y4y17"age = 24return //相当于return name,age
}
func main() {name, age := test()fmt.Printf("name: %v\n", name)fmt.Printf("age: %v\n", age)
}
package mainimport "fmt"func test() (name string, age int) {n := "Y4y17" //重新定义了n和a,那么返回的时候只能返回n和a,而不是name和agea := 24 //这种情况一般不这样写,一般就是直接去掉返回值中的name和age,只保留类型return n, a
}
func main() {name, age := test()fmt.Printf("name: %v\n", name)fmt.Printf("age: %v\n", age)
}
Go中经常会使用其中一个返回值作为函数是否执行成功、是否有错误信息的判断条件;例如return value,exists、return value,ok、return value,err等
当函数的返回值过多的时候,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如,同类型的返回值可以放进slice中,不同类型的返回值可以放进map中。
当函数有多个返回值的时候,如果其中某个或者是某几个返回值不想用,可以通过下划线_来丢弃这些返回值。
函数的参数
package mainimport "fmt"func test(a int) {a = 200 //test函数中修改变量a的值为200
}
func main() {a := 5 //定义变量a的值为5test(a)fmt.Printf("a: %v\n", a)//输出a的值,依然还是5
}
数组、切片、指针都是传地址,也就是传址的方式;
package mainimport "fmt"func test(s []int) {s[2] = 17
}
func main() {s := []int{1, 2, 3, 4}test(s)fmt.Printf("s: %v\n", s)//输出的结果为s: [1 2 17 4]
}
变长参数
package mainimport "fmt"
//其实就像是传递了一个数组或者是切片
func test(args ...int) {for _, v := range args {fmt.Printf("v: %v\n", v)}
}
func main() {test(1, 2, 3, 4, 5, 6, 7)
}
package mainimport "fmt"func test(name string, b bool, args ...int) {fmt.Printf("name: %v\n", name)fmt.Printf("b: %v\n", b)for _, v := range args {fmt.Printf("v: %v\n", v)}
}
func main() {test("Y4y17", true, 1, 2, 3, 4, 5, 6, 7)
}
函数中的type关键字
相当于在c语言中的typedef关键字,说白了就是对现有的数据类型起了一个别名
package mainimport "fmt"func add(a int, b int) int {return a + b
}
func comp(a int, b int) int {if a > b {return a} else {return b}
}
func main() {type f func(a int, b int) intff := addr := ff(1, 2)fmt.Printf("r: %v\n", r)ff = compr = ff(1, 2)fmt.Printf("r: %v\n", r)
}
Go高阶函数
函数作为参数
package mainimport "fmt"func test(name string, f func(string)) { f(name)
}
func sayHello(name string) {fmt.Printf("Hello: %v\n", name)
}
func main() {test("Y4y17", sayHello)
}
//test函数接受两个参数,分别是string类型的name和func(函数类型)的f(函数名)
sayHello函数接受一个参数,即string类型的name,主函数中调用test函数,传递参数Y4y17,和sayHello函数名;那么test函数中便会执行sayHello("Y4y17")
函数作为返回值
package mainimport "fmt"func add(a int, b int) int {return a + b
}
func sub(a int, b int) int {return a - b
}
func test(operate string) func(int, int) int {//test函数接受一个string类型的参数operate,test函数返回值是一个函数,该函数有两个int类型的参数并且返回值也是int类型switch operate {case "+"://当operate是+的时候,返回add方法return addcase "-"://当operate是-的时候,返回sub方法return subdefault:return nil}
}func main() {f := test("-")res := f(1, 2)fmt.Printf("res: %v\n", res)
}
匿名函数
前面提到了在函数中是不允许嵌套函数的,但是我们可以使用匿名函数,来实现一些简单的功能:
package mainimport "fmt"func main() {//匿名函数和普通函数的区别其实就是没有了函数的名字,当在一个函数中不写函数名的时候就是匿名函数max := func(a int, b int) int {if a > b {return a} else {return b}}(1, 2) //直接在最后面加上()以及实参的时候,就是自己调用自己//r := max(3, 8) //fmt.Printf("r: %v\n", r)fmt.Printf("max: %v\n", max)
}
Golang闭包
闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁,或者是说函数和其引用环境的组合体
闭包指的是一个函数和与其相关的应用环境组合而成的实体。简单来说,闭包=函数+引用环境。看下面的例子:
package mainimport "fmt"//定义一个函数名为test,该函数返回值是一个函数,这个函数有一个int类型的参数 并且他返回值是一个int类型的数
func test() func(int) int {var x intreturn func(i int) int {x += ireturn x}
}
func main() {f := test()//f赋值为test的返回值,就是返回的函数,初始x为0fmt.Printf("f(10): %v\n", f(10)) //计算0+10=>10 这里x就变成了10 而不是0fmt.Printf("f(20): %v\n", f(20)) //计算10+20=>30 这里x就变成了30 而不是10fmt.Printf("f(30): %v\n", f(30)) //计算30+30=>60 这里x就变成了60 而不是30
}
变量f是一个函数并且他引用了其外部作用域中的x变量,此时f就是一个闭包。在f的生命周期内,变量x也一直有效;
package mainimport ("fmt""strings"
)func makeSuffixFunc(suffic string) func(string) string {return func(name string) string {//这里的strings.HasSuff(name.suffic)表示判断name是否以suffic结尾//如果是返回true,否则false 需要导包stringsif !strings.HasSuffix(name, suffic) {return name + suffic} else {return name}}
}
func main() {f := makeSuffixFunc("World")str := f("Hello")fmt.Printf("str: %v\n", str)
}
package mainimport "fmt"func cal(base int) (func(int) int, func(int) int) {add := func(a int) int {base += areturn base}sub := func(a int) int {base -= areturn base}return add, sub
}
func main() {add, sub := cal(10)res := add(10)fmt.Printf("res: %v\n", res)res = sub(5)fmt.Printf("res: %v\n", res)
}
定义了cal函数,cal函数存在一个形参base,cal函数返回值存在两个,并且这两个都是函数,之后在cal函数中定义两个匿名的函数分别赋值给add 和 sub,最终cal函数返回这两个匿名函数,之后在main函数中,令add sub为cal(10) ,其中base就是10 之后执行res=add(10),而这个10是匿名函数的形参a=10,执行base+=a故base=20
base的值会存储下来,之后res = sub(5),此时sub函数中的形参a就是5,再次执行base-=a,故得到base=15
Go语言中的递归函数
函数内部调用函数自身的函数称之为递归函数
使用递归函数最重要的三点:
递归就是自己调用自己
必须先定义函数的退出条件,没有退出条件,递归将成为死循环
go语言递归函数很可能会产生一大堆的goroutine,也很可能会出现占空间内存溢出的问题
package mainimport "fmt"//数的阶乘
func fc(n int) int {if n <= 1 {return 1} else {res := n * fc(n-1)return res}
}
func main() {res := fc(5)fmt.Printf("res: %v\n", res)
}
package mainimport "fmt"
//f(n)=f(n-1)+f(n-2) f(2)==f(1)==1
func fb(n int) int {if n <= 2 {return 1} else {res := fb(n-1) + fb(n-2)return res}
}
func main() {fmt.Printf("fb(3): %v\n", fb(3))
}
Go语言中的defer语句
go语言中的defer语句会将其后面跟随的语句进行延时处理,在defer归属的函数即将返回时,姜堰市处理的语句按defer定义的逆序进行执行;也就是说先被defer的语句最后被执行,最后被defer的语句,最先被执行
package mainimport "fmt"func main() {fmt.Printf("start...\n")defer fmt.Printf("stop1...\n")defer fmt.Printf("stop2...\n")defer fmt.Printf("stop3...\n")fmt.Printf("END...\n")
}
输出结果如下:

Go语言中的init函数
golang有一个特殊的函数init函数,先于main函数的执行,主要实现包级别的一些初始化操作
init函数额主要特点
init函数先于main函数自动执行,不能被其他函数调用
init函数没有输入参数、返回值
每个包可以有多个init函数
包的每个源文件也可以有多个init函数,这点比较特殊
同一个包的init执行顺序,golang没有明确的定义;
不同包的init函数按照包导入的依赖关系决定执行顺序
初始化顺序:变量初始化>init()>main()
package mainimport "fmt"var i int = initVar()func initVar() int {fmt.Printf("initvar...\n")return 100
}
func init() {fmt.Printf("init2...\n")
}
func init() {fmt.Printf("init...\n")
}
func main() {fmt.Printf("start...\n")
}
输出结果为:

同时存在多个init()的时候,遵循自上而下的执行顺序