目录
前言
在Go语言中,函数是基本的代码组织和复用单元。函数可以接受输入参数,执行特定任务,并返回结果。Go语言中的函数设计简洁且功能强大,支持多返回值、匿名函数、闭包等特性。本文将详细介绍GO语言当中的函数🧐
1.函数基本声明与调用🌻
Go语言中定义函数的基本语法如下:
func 函数名(参数列表) 返回值类型 {
// 函数体
}
func
:关键字,用于声明函数。函数名:函数的名称,遵循标识符命名规则。
参数列表:函数的输入参数,格式为
参数名 类型
,多个参数用逗号分隔。返回值类型:函数的返回类型。如果函数没有返回值,可以省略;如果有多个返回值,需要用括号括起来。
函数体:函数的具体实现逻辑。
💥示例代码:
func add(a int, b int) int { // 接受两个int类型的参数a和b,并返回它们的和
return a + b
}
result := add(3, 5) // 通过函数名和参数列表来调用函数
fmt.Println(result) // 输出: 8
2. 多个返回值的函数🌻
Go语言支持函数返回多个值,这是Go语言的一个特色功能。多返回值的函数定义如下:
func 函数名(参数列表) (返回值类型1, 返回值类型2, ...) {
// 函数体
return 值1, 值2, ...
}
💥示例代码:
func swap(x, y string) (string, string) {
return y, x
}
a, b := swap("hello", "world")
fmt.Println(a, b) // 输出: world hello
如果某个返回值不需要使用,可以用下划线_
忽略:
_, b := swap("hello", "world")
并且Go语言允许为返回值命名,这样在函数体中可以直接使用这些变量,且return
语句可以省略返回值。
💥示例代码:
func divide(a, b float64) (quotient float64, err error) { // 定义返回值名称
if b == 0 {
err = errors.New("除数不能为0")
return
}
quotient = a / b
return // 省略返回值
}
result, err := divide(10, 2)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(result) // 输出: 5
}
3.函数参数传递方式🌻
函数如果使用参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。调用函数,可以通过两种方式来传递参数:
传递类型 | 描述 |
---|---|
值传递 | 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 |
引用传递 | 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。 |
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
3.1 值传递🌷
-
基本原理:当使用值传递时,函数接收的是实际参数的一个副本。函数内部对这个副本的修改不会影响到原始的实际参数。这类似于在函数内部创建了一个局部变量,该变量的初始值是实际参数的值。
package main
import "fmt"
func modifyValue(x int) {
x = 100 // 修改函数内部的副本
fmt.Printf("Inside function: x = %d\n", x)
}
func main() {
num := 10
fmt.Printf("Before function call: num = %d\n", num)
modifyValue(num)
fmt.Printf("After function call: num = %d\n", num)
}
上述代码最终输出结果如下:
Before function call: num = 10
Inside function: x = 100
After function call: num = 10
解释🪷:
在
main
函数中,num
的值为10
。调用
modifyValue(num)
时,num
的值被复制到函数的参数x
中。在
modifyValue
函数中,x
的值被修改为100
,但这只是函数内部的副本,不会影响到main
函数中的num
。因此,
main
函数中的num
仍然保持为10
。
3.2 引用传递 🌷
-
基本原理:引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。引用传递指针参数传递到函数内。
package main
import "fmt"
func modifyValue(x *int) { // 接收一个指针
*x = 100 // 通过指针修改原始变量
fmt.Printf("Inside function: x = %d\n", *x)
}
func main() {
num := 10
fmt.Printf("Before function call: num = %d\n", num)
modifyValue(&num) // 传递变量的地址
fmt.Printf("After function call: num = %d\n", num)
}
上述代码最终输出结果如下:
Before function call: num = 10
Inside function: x = 100
After function call: num = 100
解释🪷:
在
main
函数中,num
的值为10
。调用
modifyValue(&num)
时,传递的是num
的地址。在
modifyValue
函数中,通过*x
(解引用)直接修改了num
的值。因此,
main
函数中的num
的值被修改为100
。
4.可变参数函数 🌻
Go语言支持可变参数函数,即函数的参数数量不固定。可变参数通过...类型
来定义,函数内部会将可变参数视为切片。
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
fmt.Println(sum(1, 2, 3)) // 输出: 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 输出: 15
5.GO语言函数作为实参🌻
Go 语言可以很灵活的创建函数,并作为另外一个函数的实参。
💥示例代码:
package main
import (
"fmt"
"math"
)
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9)) // 输出9
}
6.匿名函数与闭包🌻
Go语言支持匿名函数,即没有函数名的函数。匿名函数可以直接定义并调用,也可以赋值给变量。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。而闭包是指一个函数捕获并保存了其外部作用域的变量。闭包在Go语言中非常有用,常用于实现函数工厂或延迟计算。
package main
import "fmt"
func main() {
// 定义一个匿名函数并将其赋值给变量add
add := func(a, b int) int {
return a + b
}
// 调用匿名函数
result := add(3, 5)
fmt.Println("3 + 5 =", result)
// 在函数内部使用匿名函数
multiply := func(x, y int) int {
return x * y
}
product := multiply(4, 6)
fmt.Println("4 * 6 =", product)
// 将匿名函数作为参数传递给其他函数
calculate := func(operation func(int, int) int, x, y int) int {
return operation(x, y)
}
sum := calculate(add, 2, 8)
fmt.Println("2 + 8 =", sum)
// 也可以直接在函数调用中定义匿名函数
difference := calculate(func(a, b int) int {
return a - b
}, 10, 4)
fmt.Println("10 - 4 =", difference)
}
上述代码最终输出结果为:
3 + 5 = 8
4 * 6 = 24
2 + 8 = 10
10 - 4 = 6
7.高阶函数🌻
高阶函数是指可以接受函数作为参数或返回函数的函数。Go语言中函数是一等公民,可以作为值传递。
func apply(op func(int, int) int, a, b int) int {
return op(a, b)
}
result := apply(func(a, b int) int {
return a + b
}, 3, 5)
fmt.Println(result) // 输出: 8
解释🪷:
apply
是一个函数,它接收三个参数:
op
:这是一个函数类型的参数,其类型为func(int, int) int
。这意味着op
是一个函数,它接收两个整数参数并返回一个整数。
a
和b
:这两个是普通的整数参数。
在
apply
函数内部,调用了op(a, b)
,并将结果返回。这里的
op
是一个函数,apply
函数的作用是将a
和b
作为参数传递给op
,并返回op
的执行结果。result := apply(func(a, b int) int { return a + b }, 3, 5)
在调用
apply
函数时,传递了一个匿名函数作为op
参数。匿名函数的定义如下:
func(a, b int) int { return a + b }
这是一个函数字面量,它接收两个整数参数
a
和b
,并返回它们的和。
8.递归函数 🌻
Go语言支持递归函数,即函数调用自身。递归函数通常用于解决分治问题,如计算阶乘、斐波那契数列等。以下例子为一个计算阶乘的代码:
func factorial(n int) int {
if n == 0 {
return 1
}
return n * factorial(n-1)
}
fmt.Println(factorial(5)) // 输出: 120
9.延迟调用defer 🌻
Go语言中的defer
语句用于延迟函数的执行,直到包含它的函数返回为止。defer
通常用于资源释放、关闭文件等操作。
🌱基本语法:
defer functionCall()
defer
关键字后面跟着一个函数调用。被
defer
的函数调用会在其所在的函数返回之前执行,无论该函数是正常返回还是因为错误而返回。
🌱defer执行时机:
当
defer
语句被执行时,函数调用不会立即执行,而是被推迟到其所在的函数返回之前。如果一个函数中有多个
defer
语句,它们会按照 后进先出(LIFO) 的顺序执行,即最后一个defer
的函数调用会最先执行。
package main
import "fmt"
func main() {
defer fmt.Println("First defer")
defer fmt.Println("Second defer")
defer fmt.Println("Third defer")
fmt.Println("Function body")
}
上述代码输出如下:
Function body
Third defer
Second defer
First defer
defer
语句按照它们出现的顺序被压入栈中。当函数返回时,defer
的函数调用按照后进先出的顺序执行。
💥示例——关闭文件:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close() // 确保文件在函数返回时关闭
// 在这里读取文件内容
fmt.Println("File opened successfully.")
}
解释🪷:
os.Open
打开一个文件,返回一个文件对象和可能的错误。使用
defer file.Close()
确保文件在main
函数返回时被关闭,无论是否发生错误。如果在读取文件内容时发生错误,
file.Close()
仍然会被调用。
10.GO语言常用的内置函数 🌻
函数签名 | 功能描述 |
---|---|
append(slice []T, elems ...T) []T | 向切片追加元素,返回新的切片 |
cap(x) int | 返回切片、数组或通道的容量 |
close(c chan<- Type) | 关闭通道,使其不能再发送数据 |
complex(real, imag float64) complex128 | 创建一个复数 |
copy(dst, src []Type) int | 将 src 的内容复制到 dst ,返回复制的元素数量 |
delete(m map[Type]Type, key Type) | 从映射中删除指定的键 |
imag(c complex128) float64 | 返回复数的虚部 |
len(x) int | 返回切片、数组、字符串或映射的长度 |
make([]T, len, cap) | 创建切片、映射或通道 |
new(Type) | 分配内存,返回指向分配内存的指针 |
panic(v interface{}) | 触发运行时错误,终止当前函数的执行 |
recover() | 恢复从 panic 中恢复,仅在 defer 函数中有效 |
real(c complex128) float64 | 返回复数的实部 |
这些函数是 Go 语言的内置函数,可以直接在代码中使用,无需额外导入。