Golang学习笔记(五):函数
文章深入探讨了Golang中函数的核心设计,包括通过func关键字声明的基本语法、多返回值机制以及命名返回参数的使用。内容涵盖了参数传递的底层逻辑(传值与传指针的区别),并演示了如何通过…展开slice实现不定长变参。此外,详细讲解了匿名函数、闭包以及将函数作为类型和值进行传递的高级用法,为理解Go语言函数式编程范式提供了全面指导。
函数基本语法与声明
在Go语言中,函数是核心的代码组织单元,通过 func 关键字进行声明。函数的声明不需要前置原型,且左大括号 { 必须紧跟函数名,不能另起一行。
第1步:定义不同类型的函数
Go语言支持灵活的重载与重载组合(虽然不支持方法重载,但支持参数数量不同的同名函数在不同上下文使用,不过通常不推荐),具体包括:
- 无参数无返回值:
// 一般函数
func func_name(a int) {
println(a)
}
- 多参数无返回值:
// 多参数,无返回值
func func_name(a, b int, c string) {
println(a, b, c)
}
- 单个返回值:对于相同类型的返回值,可以省略类型声明。
// 单个返回值
func func_name(a, b int) int {
return a + b
}
- 多个返回值:Go支持多返回值,常用于返回结果和错误信息。
// 多个返回值,可命名返回参数
func func_name(a, b int) (c int, err error) {
return a+b, nil
}
第2步:了解函数限制与特性
Go语言在设计上做出了一些取舍:
- 不支持:嵌套函数(nested functions)、函数重载(overload)、默认参数(default parameters)。
- 支持:无需声明原型、不定长度变参、多返回值、命名返回值参数、匿名函数和闭包。
包的可见性由函数名的首字母决定:小写字母开头的函数仅在包内可见,大写字母开头的函数可以被其他包调用。
多返回值及命名返回参数
Go语言允许函数返回多个值,这与Python类似,但返回值不是元组(tuple),而是逗号分隔的值列表。对于不再需要的返回值,可以使用空白标识符 _ 丢弃。
第3步:使用命名返回参数
当返回参数被命名时,即使没有具体的返回值表达式,return 语句也会自动返回这些变量当前的值。但这要求变量在函数体内被正确赋值,或者显式地返回。
package main
func change(a, b int) (x, y int) {
x = a + 100
y = b + 100
return // 隐式返回 x 和 y 的当前值,即 101, 102
}
func main(){
a := 1
b := 2
c, d := change(a, b)
println(c, d)
}
注意:如果命名返回参数在函数体内被局部变量遮蔽(shadowing),则必须使用显式的 return 语句来指定返回值。
参数传递:传值与传指针
Go语言中的函数参数传递默认是传值(Pass by Value),即复制一份变量的值传给函数。若要修改原变量的值或高效传递大对象,需要使用传指针(Pass by Pointer)。
第4步:理解指针操作
Go保留了指针,使用 & 取地址,使用 * 间接访问目标对象的成员。指针操作使用 . 而非 ->。
// 传值示例:副本改变,原值不变
func add1(a int) int {
a = a + 1
return a
}
x := 3
x1 := add1(x)
// x 仍为 3,x1 为 4
// 传指针示例:原值被修改
func add2(a *int) int {
*a = *a + 1
return *a
}
x := 3
x1 := add2(&x)
// x 变为 4,x1 为 4
优势与应用场景:
1. 效率:指针大小为8字节,传递大结构体时使用指针可以避免昂贵的内存拷贝。
2. 共享状态:多个函数可以通过指针操作同一个对象。
3. 特殊类型:对于 string、slice、map 这三种类型,其内部实现机制类似于指针,可以直接传递而无需额外取地址。但若需要修改 slice 的长度,仍需传递其指针。
可变参数与匿名函数
可变参数本质上是一个切片(Slice),通常位于参数列表的最后。任意类型的不定参数可以使用 interface{} 接收。
第5步:处理不定长度变参
使用 ...type 定义可变参数。调用时,如果是同类型的多个参数直接传入;如果是已有的切片,需要使用 ... 展开,否则该切片会被当作单个参数处理。
package main
func sum(s string, args ...int) {
var x int
for _, n := range args {
x += n
}
println(s, x)
}
func main(){
// 直接传入多个整数
sum("1+2+3=", 1, 2, 3)
// 展开切片
x := []int{0, 1, 2, 3, 4}
sum("0+1+2+3=", x[:4]...)
}
第6步:掌握匿名函数
匿名函数没有名称,通常用于需要传递简单逻辑的场景。
f := func(x, y int) int {
return x + y
}
函数作为值和类型
在Go语言中,函数是一等公民,可以作为值存储在变量中,也可以自定义函数类型。这为实现回调、策略模式等功能提供了便利。
第7步:定义和使用函数类型
可以使用 type 关键字定义一个新的函数类型,所有拥有相同参数和返回值的函数都兼容此类型。
// 声明了一个函数类型,输入int,输出bool
type testInt func(int) bool
可以将这种类型的函数作为参数传递,例如编写一个过滤函数:
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func isOdd(integer int) bool {
return integer%2 != 0
}
// 使用
filter([]int{1, 2, 3}, isOdd)
第8步:函数作为值的传递
函数可以是nil,可以赋值给变量,也可以作为参数传递给另一个函数(高阶函数)。
package main
// 定义函数类型 callback
type callback func(string)
// 接受另一个函数作为参数的函数
test := func(a, b int, calc func(int, int) int) {
println(calc(a, b))
}
func main(){
// 将匿名函数赋值给变量
var cb callback
cb = func(s string) {
println(s)
}
cb("hello world")
// 直接传递匿名函数
test(1, 2, func(a, b int) int { return a + b })
}免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









