Go 语言 函数、init函数、匿名函数的使用
一、函数:
1.基本概念:
- 完成某一个功能的程序指令(语句)的集合,称为函数。
- 在 go 中,函数分为:自定义函数,系统函数。
函数基本语法:
func 函数名 (
形参列表
) (返回值列表
) { 执行语句…
return 返回值列表
}
简单栗子:
- 将简单的计算器功能封装成一个函数;
package main
import (
"fmt"
)
func main(){
var num1 float64
var num2 float64
var operator01 string
fmt.Println("输入值:")
fmt.Scanln(&num1)
fmt.Println("输入运算符号")
fmt.Scanln(&operator01)
fmt.Println("输入值:")
fmt.Scanln(&num2)
result := calculator(num1, num2, operator01)
fmt.Println("最终结果为:", result)
}
// 定义一个计算器的函数, 函数名小写, 因为在此包中引用
// 有函数名;函数接收的形参;返回值列表;执行语句;return 返回值列表
func calculator(n1 float64, n2 float64, operator string) (float64){
var i float64 = 0
switch operator {
case "+":
i = n1 + n2
case "-":
i = n1 - n2
case "*":
i = n1 * n2
case "/":
i = n1 / n2
default:
fmt.Println("输入超出范围")
}
return i
}
2.函数调用过程:
举个栗子:
package main
import (
"fmt"
)
func test(n1 int) {
n2 := n1 + 1
fmt.Println("test()函数 n2=", n2)
}
func main(){
n1 := 20
// 调用test()函数
test(n1)
fmt.Println("main()函数 n1=", n1)
}
// 输出结果:
test()函数 n2= 21
main()函数 n1= 20
分析上述流程:
- 调用一个函数时,会给函数分配一个新的空间,编译器会通过自身的处理,让这个新的空间和其他的栈的空间区分开。
- 每个函数对应的栈中,数据空间是独立的,不会混淆。
- 当一个函数调用完成后,程序会销毁这个函数对应的栈空间。
3.return 语句:
基本语法:
func 函数名 (
形参列表
) (返回值列表
) { 执行语句…
return 返回值列表
}
- 若返回多个值时,在接收时,希望忽略某个值,则用
_
符号表示占位忽略。 - 若返回值只有一个,可以不写() 括号。
举个栗子:
-
返回多个值的栗子:
func main(){ fmt.Println("main()函数") num1 := 20 num2 := 10 // 调用count()函数 n3, n4 := count(num1, num2) fmt.Printf("count函数的返回值n3=%d, n4=%d", n3, n4) } // 此函数返回两个值 func count(n1 int, n2 int) (int, int) { fmt.Println("进入count函数") n3 := n1 + n2 n4 := n1 - n2 return n3, n4 } // 输出如下 main()函数 进入count函数 count函数的返回值n3=30, n4=10
-
多个值,不接收,使用 _ 表示占位忽略 栗子:
func main(){ fmt.Println("main()函数") num1 := 20 num2 := 10 // 调用count()函数 使用 _ 表示占位呼噜 n3, _ := count(num1, num2) fmt.Printf("count函数的返回值n3=%d\n", n3) } // 此函数返回两个值 func count(n1 int, n2 int) (int, int) { fmt.Println("进入count函数") n3 := n1 + n2 n4 := n1 - n2 return n3, n4 } //输出结果: main()函数 进入count函数 count函数的返回值n3=30
-
返回值只有一个,(返回值类型列表) 可以不写 ():
func main(){ fmt.Println("main()函数") num1 := 20 num2 := 10 n5 := sum(num1, num2) fmt.Printf("sum函数的返回值n5=%d\n", n5) } // 返回值只有一个, 可以不写 (返回值类型列表) 这个 "括号" func sum(n1 int, n2 int) int { fmt.Println("进入sum函数") n5 := n1 + n2 return n5 } // 输出结果: main()函数 进入sum函数 sum函数的返回值n5=30
二、递归函数:
一个函数在函数体内又调用了本身,我们称为递归调用
。
举个栗子:
package main
import (
"fmt"
)
// 写一个递归函数
func test (n1 int) {
if n1 > 2 {
n1--
test(n1)
}
fmt.Println("n1=", n1)
}
func main(){
test(4)
}
// 输出结果:
n1= 2
n1= 2
n1= 3
注意事项:
- 执行一个函数时,就新建一个受保护的独立空间(新函数栈)。
- 函数的局部变量是独立的,不会相互影响。
- 递归必须得有终止条件,否则会出现无限递归。
- 一个函数执行完或遇到return,该函数本身也会被系统销毁。
三、函数注意事项及使用细节:
-
函数的形参列表可有多个,返回值列表也可以有多个。
-
形参列表和返回值列表的数据类型可以是值类型或引用类型。
-
被本包文件或其它包文件使用首字母大写,类似
public
; 只能被本包文件使用首字母小写,类似privat
。 -
函数中的变量是局部的,函数外不生效。
-
基本数据类型和数组默认都是值传参,进行值拷贝。在函数内部修改,不会影响到原来的值。
-
若在函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针方式操作变量。
// 在函数内部, 修改函数外部的变量 func update(number *int) { // *number 表示 指针指向的值(20) + 20 *number 指向的值 变成了 40 *number = *number + 20 fmt.Println("new_num", *number) } func main(){ var num int = 20 // 传进去的参数是 num 的内存地址 update(&num) fmt.Println("old_num", num) } // 输出结果: new_num 40 old_num 40
-
go 函数 不支持函数 重载,会报错
函数重复定义
。 -
在go 中,函数也是一种数据类型,可赋值给一个变量,该变量就是一个函数类型的变量。通过变量可以对函数调用。
// 函数也是一种数据类型, 可以赋值一个变量, 该变量就是一个函数类型的变量, 可以通过变量来调用函数 func sum(n1 int, n2 int) int { fmt.Println("进入sum()函数...") return n1 + n2 } func main() { var i int = 1 var j int = 2 fmt.Println("进入main()函数...") result := sum fmt.Printf("result的数据类型%T, sum的数据类型是%T\n", result, sum) new_result := result(i, j) // 等价于 new_result := sum(i, j) fmt.Println("new_result", new_result) } // 输出结果如下: 进入main()函数... result的数据类型func(int, int) int, sum的数据类型是func(int, int) int 进入sum()函数... new_result 3
-
函数可以作为形参,并且调用:
// 函数可以当作一个形参传入 func params(n1 int, n2 int) int { return n1 + n2 } func sum(params func(int, int) int, num1 int, num2 int )int { return params(num1, num2) } func main() { new_sum := sum(params, 10, 20) fmt.Println("new_sum", new_sum) } // 输出结果: new_sum 30
-
go 支持自定义数据类型:
-
案例一,自定义int类型:
func main() { // 给int 取了别名, 在go中 myInt 和 int 虽然都是 int 类型, 但是go 认为 myInt 和 int 是两个类型 type myInt int var num1 myInt var num2 int num1 = 40 num2 = int(num1) // 如果这里不转换会报错 fmt.Println("num1=", num1, "num2=", num2) } // 输出结果: num1= 40 num2= 40
-
案例二,自定义函数类型:
// 自定义一个函数 数据类型 type myFunType func (int, int) int // 将函数作为参数传递 func myFunc (funvar myFunType, n1 int, n2 int) int { return funvar(n1, n2) } // 运算函数 func sum(num1 int, num2 int) int { return num1 + num2 } func main() { var i int = 10 var j int = 20 result := myFunc(sum, i, j) fmt.Println("result=", result) } // 输出结果: result= 30
-
-
支持对函数返回值命名:
// 函数返回值命名 func sum(num1 int, num2 int) (sum int, sub int) { sub = num1 - num2 sum = num1 + num2 return } func main() { var i int = 10 var j int = 20 a, b := sum(i, j) fmt.Printf("a=%v b=%v", a, b) } // 输出结果: a=30 b=-10
-
go 支持可变参数:
-
基本语法:
func sum(args…) sum int {
// 执行语句
}
args 是 slice 切片,通过 args[index],可以访问到每个值。(args可以随意命名)
-
举个栗子,支持1到多个参数:
// go 支持可变参数, 1 到多个参数 func sum(num1 int, args... int) int { t := num1 for i := 0; i < len(args); i++ { t += args[i] } return t } func main() { count := sum(10, 20, 30, 40, 50) fmt.Printf("count=%v", count) } // 输出结果: count=150
-
四、init 函数的使用:
每一个源文件都可以包含一个 init 函数,init 函数会在main函数前被调用
。
基本使用:
// init函数 会在main 函数执行前 被调用
func init() {
fmt.Println("执行init函数()...")
}
func main(){
fmt.Println("执行main函数()...")
}
// 输出结果:
执行init函数()...
执行main函数()...
init 函数注意事项:
-
一个文件同事包含
全局变量定义、init函数、main函数
,则执行流程全局变量 —> init函数 —> main函数:// 声明一个全局变量 test var test = testFunc() func testFunc () int { fmt.Println("提供给变量的函数...") // 先执行 1 return 100 } // init函数 会在main 函数执行前 被调用 func init() { fmt.Println("执行init函数()...") // 在执行 2 } func main(){ fmt.Println("执行main函数()...") // 最后执行 3 } // 输出结果: 提供给变量的函数... 执行init函数()... 执行main函数()...
-
如果引入的包都含有
变量定义、init函数
,先执行包中的引入的全局变量、init函数:
五、匿名函数:
go 支持匿名函数。匿名函数就是没有名字的函数,若我们希望某个函数仅使用一次,就可以考虑使用匿名函数,匿名函数也可以实现多次调用。
1.基本使用:
简单使用:
-
定义匿名函数时,就直接调用(这种方式匿名函数只能被调用一次)。
func main(){ // 求两个数的和, 定义一个匿名函数, 仅能使用一次 result := func (n1 int, n2 int) int { return n1 + n2 }(10, 20) fmt.Println("result=", result) } // 输出结果: result=30
-
将匿名函数赋给一个变量,在通过该变量来调用匿名函数。
func main() { // 给匿名函数一个变量, 通过调用匿名函数的变量进行操作 // 此时 变量 i 类型就是 函数变量, 我们可以通过 i()完成调用 i := func (num1 int, num2 int) int { return num1 + num2 } result := i(10, 20) fmt.Println("result的结果为:", result) result_01 := i(30, 40) fmt.Println("result的结果为:", result_01) } // 输出结果: result的结果为: 30 result的结果为: 70
2.全局匿名函数:
如果将匿名函数赋给一个全局变量
,那么这个匿名函数就是一个全局匿名函数。
举个栗子:
// 定义一个全局的匿名函数, 变量为MyFunc
var (
MyFunc = func (num1 int, num2 int) int {
return num1 + num2
}
)
func main(){
// 调用全局的匿名函数
result := MyFunc(10, 20)
fmt.Println("result", result)
}
// 输出结果:
result 30