Go语言中的函数
go语言中函数特性
go语言有三种函数:普通函数、匿名函数(没有名称的函数)方法(定义在struct上的函数)。receiver
go语言中不运算函数重载(overload),也就是说不允许函数同名
go语言中的函数不能嵌套,但是可以嵌套匿名函数
函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数
函数可以作为参数传递给另一个函数
函数的返回值可以是另一个函数
函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数
函数参数可以没有名称
go语言中函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务,函数可以重复调用,从而达到代码的复用
func function_name([parameter_list])[return_types]{
函数体
}
package main
import "fmt"
func sum(a int, b int) (res int) {
res = a + b
return res
}
func main() {
res := sum(1, 2)
fmt.Printf("res: %v\n", res)
}
package main
import "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 main
import "fmt"
func test() {
fmt.Print("testing...")
}
func main() {
test()
}
package main
import "fmt"
func test() (string, int) {
name := "Y4y17"
age := 24
return name, age
}
func main() {
name, age := test()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
package main
import "fmt"
func test() (name string, age int) {
name = "Y4y17"
age = 24
return //相当于return name,age
}
func main() {
name, age := test()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
package main
import "fmt"
func test() (name string, age int) {
n := "Y4y17" //重新定义了n和a,那么返回的时候只能返回n和a,而不是name和age
a := 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 main
import "fmt"
func test(a int) {
a = 200 //test函数中修改变量a的值为200
}
func main() {
a := 5 //定义变量a的值为5
test(a)
fmt.Printf("a: %v\n", a)//输出a的值,依然还是5
}
数组、切片、指针都是传地址,也就是传址的方式;
package main
import "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 main
import "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 main
import "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 main
import "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) int
ff := add
r := ff(1, 2)
fmt.Printf("r: %v\n", r)
ff = comp
r = ff(1, 2)
fmt.Printf("r: %v\n", r)
}
Go高阶函数
函数作为参数
package main
import "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 main
import "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 add
case "-"://当operate是-的时候,返回sub方法
return sub
default:
return nil
}
}
func main() {
f := test("-")
res := f(1, 2)
fmt.Printf("res: %v\n", res)
}
匿名函数
前面提到了在函数中是不允许嵌套函数的,但是我们可以使用匿名函数,来实现一些简单的功能:
package main
import "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 main
import "fmt"
//定义一个函数名为test,该函数返回值是一个函数,这个函数有一个int类型的参数 并且他返回值是一个int类型的数
func test() func(int) int {
var x int
return func(i int) int {
x += i
return x
}
}
func main() {
f := test()
//f赋值为test的返回值,就是返回的函数,初始x为0
fmt.Printf("f(10): %v\n", f(10)) //计算0+10=>10 这里x就变成了10 而不是0
fmt.Printf("f(20): %v\n", f(20)) //计算10+20=>30 这里x就变成了30 而不是10
fmt.Printf("f(30): %v\n", f(30)) //计算30+30=>60 这里x就变成了60 而不是30
}
变量f是一个函数并且他引用了其外部作用域中的x变量,此时f就是一个闭包。在f的生命周期内,变量x也一直有效;
package main
import (
"fmt"
"strings"
)
func makeSuffixFunc(suffic string) func(string) string {
return func(name string) string {
//这里的strings.HasSuff(name.suffic)表示判断name是否以suffic结尾
//如果是返回true,否则false 需要导包strings
if !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 main
import "fmt"
func cal(base int) (func(int) int, func(int) int) {
add := func(a int) int {
base += a
return base
}
sub := func(a int) int {
base -= a
return 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 main
import "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 main
import "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 main
import "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 main
import "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()的时候,遵循自上而下的执行顺序