函数是基本的代码块,用于执行一个任务,是构成代码执行的逻辑结构。
在Go语言中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。
函数定义
每个go程序都是从名为main的package包的main()函数开始执行包的概念不是这里的重点,以后做单独说明。同时main()函数是无参数,无返回值的。
格式
func funcName(形参1 type[, 形参2 type...]) [([[返回变量1] type[, [返回变量2] type...]])] {
[return [返回变量1[, 返回变量2...]]]
}
a. 如果形参类型都一样,可以这样写: 形参1, 形参2 type, 同时返回变量也一样
b. 如果只有一个返回值或者无返回值, 可以去掉对应的()
c. 如果返回有返回值,该函数中最外层要有return语句
d. 返回语句(), 返回变量名可以省略
e. []表示可省略
f. 不支持默认参数
package main
import "fmt"
func printName(name1 string, name2 string) {
fmt.Println("name1 = ", name1, " ", "name2 = ", name2)
}
func main() {
printName("张三", "李四")
}
输出为:
name1 = 张三 name2 = 李四
package main
import "fmt"
// 如果有多个参数,并且参数类型一致,可以省略掉前面的参数的类型
func printName(name1, name2 string) {
fmt.Printf("%T\t%T\n", name1, name2)//输出验证name1, name2的类型是否为string
fmt.Println("name1 = ", name1, " ", "name2 = ", name2)
}
func main() {
// var a,b,c,d int;
printName("张三", "李四")
}
输出为:
string string
name1 = 张三 name2 = 李四
作用域
在 Go 中,定义在函数外的变量是全局的,那些定义在函数内部的变量,对于函数来说是局部的。如果命名覆盖 — 一个局部变量与一个全局变量有相同的名字 — 在函数执行的时候,局部变量将覆盖全局变量。
返回值
函数的返回值可以是一个也可以是多个
package main
import "fmt"
// func关键字
// aAddB函数名
// a、b 参数
// int 返回值类型
//下面是几种返回值类型与返回值的写法:
func aAddB(a, b int) int {
return a + b
}
// func aSubB(a, b int) int {
// return a - b
// }
// func aSubB(a, b int) (int) {
// return a - b
// }
// func aSubB(a, b int) (s int) {
// s = a - b
// return s
// }
// func aSubB(a, b int) (s int) {
// s = a - b
// return
// }
func aSubB(a, b int) (s int) {
return a - b
}
func main() {
num := aAddB(5, 6)
fmt.Println(num)
fmt.Println(aSubB(5, 6))
}
多个返回值
go支持多个返回值
如: for k, v := range array ...
a. 如果不想要某个返回值,可以用 "_" 代替,如 for _, v := range array...,不能直接写为这样 for , v :=range array...,但可以省略 v(值), 写为 for k := range array...
b. 如果普通函数返回n个值时,不能用少于n个变量去接收
c. 不能直接打印有多个返回值的函数调用结果
d. 如果定义了返回值的变量名,就可以省略 return 语句后面的返回列表
函数参数
函数定义时指出,函数定义时有参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。
但当调用函数,传递过来的变量就是函数的实参,函数可以通过两种方式来传递参数:
值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
变参
接受变参的函数是有着不定数量的参数的。其函数定义形式如下:
func funcname(arg ... type) { }
arg ... type 告诉 Go 这个函数接受不定数量的参数。注意,在函数体中,变量 arg 是一个type类型的 slice,可以使用 range 遍历,也可以将其作为实参全部或者部分传递给调用函数。
package main
import "fmt"
// 传的是一个slice,slice分片
func sortSlice(numbers []int) []int {
fmt.Println(numbers)
//冒泡排序
for j := 0; j < len(numbers)-1; j++ {
for i := 0; i < len(numbers)-1-j; i++ {
if numbers[i] > numbers[i+1] {
temp := numbers[i]
numbers[i] = numbers[i+1]
numbers[i+1] = temp
}
}
}
return numbers
}
func main() {
// 排序前
numbers := []int{23, 45, 12, 56, 78, 89}
// 排完序以后的numbers
numbers = sortSlice(numbers)
fmt.Println(numbers)
}
package main
import "fmt"
// 动态参数
func sortSlice(numbers ...int) []int {
fmt.Println(numbers)
//冒泡排序
for j := 0; j < len(numbers)-1; j++ {
for i := 0; i < len(numbers)-1-j; i++ {
if numbers[i] > numbers[i+1] {
temp := numbers[i]
numbers[i] = numbers[i+1]
numbers[i+1] = temp
}
}
}
return numbers
}
func main() {
// 排序前
numbers := []int{23, 45, 12, 56, 78, 89}
// 排完序以后的numbers
numbers = sortSlice(numbers...)
fmt.Println(numbers)
}
函数作为值
package main
import "fmt"
// 内容:
// 函数本身也是一个类型
// 函数也有变量
// 函数也可以赋值
var a int = 9
// greating : 函数变量
// 函数值:
// func () {
// fmt.Println("Hello World!")
// }
// 全局的函数的声明
func greating() {
fmt.Println("Hello World!")
}
// func(int, int) int
// 判断函数类型的方法:
// 只需要把函数名和其他行参名字去掉,剩下的就是函数类型
func addAB(a int, b int) (s int) {
return a + b
}
func aSubB(a, b int) int {
return a - b
}
func main() {
greating()
// 函数赋值
greating1 := greating
greating1()
// 打印类型
fmt.Printf("%T \t %T\n", greating1, greating)
/*
Hello World!
Hello World!
func() func()
*/
fmt.Printf("%T\n", addAB)
}
匿名函数
匿名函数即没有函数名的函数,只能放在函数中,可以实现函数嵌套定义的功能。
package main
import "fmt"
func main() {
// greating局部变量
greating := func() {
fmt.Println("Hello World!")
}
// 只有调用函数的时候,函数体里面的代码才会被执行
greating()
// 函数值,匿名函数
func() {
fmt.Println("函数值,匿名函数!")
}()
}
返回值为函数
package main
import "fmt"
func main() {
fmt.Println(printName("张三"))
// 函数赋值
name := printName
fmt.Println(name("李四"))
// 局部变量
name1 := func(name string) string {
return name
}
fmt.Println(name1("王五"))
// 函数里面返回函数
// func(string) string
name2 := makeNameFunc()
fmt.Printf("name2的类型为:%T\n", name2)
fmt.Println(name2("赵六"))
}
// 返回值为函数
// 返回的函数的类型为func(string) string
//函数类型:func() func(string) string
func makeNameFunc() func(string) string {
return func(name string) string {
return name
}
}
func printName(name string) string {
return name
}
输出为:
张三
李四
王五
name2的类型为:func(string) string
赵六
匿名函数/闭包
varName := func() {...}
varName() //调用匿名函数
//匿名函数也可以作为一个返回值赋值给变量
func func1(m int) func(int) int {
return func2(n int) int {
return m + n
}
}
varName := func1(m) //func1(m)返回的结果就是func2函数
varName(n)//就相当于下面这个函数
func2(n int) int {
return m + n//m在func1(m)已确定值
}
example code list
import "fmt"
func main() {
//5. 函数作为值/类型
//匿名函数
varFunc := testFunc
varFunc()
//闭包
varFunc1 := closure(10)
fmt.Println(varFunc1(20)); //30
/**************************/
/*运行结果: */
/*func as a var */
/*30 */
/**************************/
}
func testFunc() {
fmt.Println("func as a var")
}
func closure(m int) func(int) int {
return func(n int) int {
return m + n
}
}
回调函数
函数也可以作为另一个函数的参数,即回调函数。回调函数需要调用函数的形参格式和被调用函数原型相同。
package main
import "fmt"
// 函数类型:func ( []int, func ())
// 参数一类型:[]int
// 参数二:func ([]int)
func method(array []int, callback func([]int)) {
callback(array)
}
func main() {
a := []int{2, 5, 1, 67, 89}
callback := func(array []int) {
fmt.Println(array)
}
fmt.Println("========")
method(a, callback)
}
递归的使用
package main
import "fmt"
// 10的阶乘
// 10*9*8*..*2*1
// 10 * 9*jieceng(8)
func jieceng(num int) int {
if num == 1 {
return 1
}
return num * jieceng(num-1)
}
func main() {
num := jieceng(10)
fmt.Println(num)
}
defer
defer声明的语句会存在类型于栈的结构中,先进后出,在函数快执行完毕(或者return)时执行,类型于析构函数
格式
defer 表达式|函数
常用于文件关闭等操作,还有重要的 recover 函数
package main
import "fmt"
func printWord(s string) {
fmt.Println(s)
}
func main() {
defer printWord("World!")
printWord("Hello!")
defer printWord("Wecome!")
printWord("To Beijing!")
defer printWord("123!")
printWord("456!")
}
输出为:
Hello!
To Beijing!
456!
123!
Wecome!
World!