函数
在Go语言中,函数是通过
func
关键字进行定义的。func hello(text string) { fmt.Println(text) } func hello1(text1, text2 string) { //如果前后参数类型相同,则可省略前面的类型定义。 fmt.Println(text1,text2) } func add(numberList ...int) { //如果要传多个同类型参数,可以这样写。 var sum int for _, i := range numberList { sum += i } } //没有返回值的函数要用return,或者直接不写。 func returntest() { return } //如果有单个返回值,则要写返回值的类型。 func returntest1() bool{ //单个也可以用命名式进行返回。 var ok bool return ok //这样声明一个值作为返回值给return返回也是可以的。 //return false } //如果有多个返回值,则要用(),在里面写返回值的类型。 func returntest2()(string, bool){ return "你好", false //这种写法不好的就是如果你多个判断有多个return,那么每次都要重新定义两个值给return回去。 } //除了上面这种多值返回,还有命名式函数返回方式 func returntest3()(val string, ok bool){ if ... { ... return val, ok } //这样命名之后返回就方便很多,精简很多 } func showtype(args ...interface{}) { //使用...可用来解序列,将函数的可变参数一个个的举出来传递给另一个可变参数的函数,不是传递可变参数本身 for _, arg := range args { switch arg.(type) { case string: fmt.Println(arg, "type is string") case int: fmt.Println(arg, "type is int") } } } /* //parameter_list(参数列表,是局部变量,非必须) //result_list(返回值列表,包含返回值名称、类型,非必须) func function_name(parameter_list) (result_list){ //函数体 } 函数可见性 1.函数首字母大写,对于所有包都是public,其他包任意调用 2.函数首字母小写,对于其他包都不能调用 */
在别的语言中,会有在函数中创建函数的写法。但在Go语言中,这种写法被优化成了匿名函数,不支持套娃函数写法。
func hello(text string) { var gethello = func() string { return "你好" } //这样就是匿名函数,通过声明一个变量等于函数进行值的获取。 //也就相当于在函数里面调用函数 fmt.Println(gethello) } func hello1(text string) { var gethello = func(hello string) { fmt.Println(hello) return } gethello("你好") }
高阶函数:
高阶函数是把一个函数作为参数传入另一个函数中,或者把一个函数传入另一个数据类型中。(难用,但好用的一个东西)
例如有一个登录小系统,有
login()
、register()
、usercenter()
三个函数,通过输入1,2,3
进入不同的界面,我们可以通过Switch case将这三个数分配给三个函数作为进入条件。var index int fmt.Scan(&index) var funcMap = map[int]func() { 1: login, 2: register, 3: usercenter, } fun, ok := funcMap[index] if ok { fun() } //Switch index { // case 1: // register() // case 2: // login() // case 3: // usercenter() //} //这里funcMap直接代替了Switch
闭包
例如:设计一个函数,西安传一个参数表示延时,后面再次传参就是将参数求和。
//func(...int) int←还要个int是因为return里还有个return,是这个函数的。
func awaitAdd(awaitSecond int) func(...int) int{
//time.Sleep(time.Duration(awaitSecond) * time.Second)
return func(numberList ...int) (sum int) {
time.Sleep(time.Duration(awaitSecond) * time.Second)
for _, i2 := range numberList {
sum += i2
}
return sum
}
}
func main() {
//方法一:在统计时间里面等待
t1 :=timeNow()
sum := awaitAdd(2)(1,2,3)
subTime := time.Since(t1)
fmt.Println(sum, subTime)
//方法二:在统计时间外面等待
add := awaitAdd(2)
t1 :=timeNow()
sum := add(1,2,3)
subTime := time.Since(t1)
fmt.Println(sum, subTime)
//结果会发现方法一和方法二有差别,方法二没了延时。因为add := awaitAdd(2)进去后直接执行了等待命令,这样sum := add(1,2,3)在计算总值时就不会算进等待的时间。
//但如果我们将awaitAdd()内的等待放进return内的函数中,这样每次调用add的时候才会延时。因为代码放的位置就会有不同的效果。
//以此类推Go语言中的函数可以一直这样嵌套,这种也就叫闭包,也可以叫高阶函数。
//像上述例子这种return里面的函数用到了外层壳awaitAdd()的参数(即awaitSecond)时,就叫闭包、
}
值传递和引用传递
讲之前先说个知识,我们存放东西在电脑里主要都是存在硬盘里;而我们在使用计算机时所见的所有操作的界面、程序啥的其实都放在内存里。(这里就是操作系统要讲的东西了)
我们打开一个软件,就是硬盘通过cpu将软件从静态的代码块变成动态的程序到内存的一个过程。
这也就意味着我们定义的每一个变量,都是将它暂时保存在了内存中。
正常的情况下来说,函数的参数是将之前块地复制了一份出来。
func change(num int) {
fmt.Println(&num) //可以看到这两个&num输出的内存地址是不一样的。
//这也就说明了函数是拷贝了一份参数的值进行操作。
num = 2 //尝试在函数中改变num的值。
}
func realchange(num *int) {
fmt.Println(&num) //这里就一样了
//num = 5 这里就不能这样改了,因为现在的num是一个内存地址
*num = 5 //只能通过内存地址去找值
}
func main() {
num := 20
fmt.Println(&num)
change(num)
fmt.Println(num) //20
realchange(num)
}
//所以如果硬是要影响原值,则就要使用引用传递。
//&是取地址。*是解引用,去这个地址指向的值。
init函数和defer函数
init函数
是一个特殊的函数,有着以下特性:
- 不能被其他函数调用,而是在main函数执行之前,自动被调用。
init函数
不能作为参数传入。init函数
不能有传入参数和返回值。- 一个Go文件可以有多个
init函数
,谁在前面谁先执行。
所以init函数
一般用于初始化。
defer函数
又称为延迟函数。
- 关键字
defer
用于注册延迟调用。 - 这些调用知道
return
前才被执行。因此可用于资源清理、异常处理、资源操作、网络编程。 - 多个
defer
语句,按先进后出的方式执行,谁离return
近谁先执行。 defer
语句中的变量,在defer
声明时就决定了。defer
函数只是延迟调用,不代表变量后改变会影响前面调用的还未改变的变量。上面的4.
也是为了证明这个。
综上这两个函数在一些实用场景里会经常用到。
评论(0)