编程语言通过函数划分不同的代码块,函数调用在底层通过栈实现。这篇笔记我们学习Go语言中函数相关的语法。
和大多数编程语言一样,main
是一个特殊的函数作为代码逻辑的入口,在Go语言中main
函数必须位于package main
中。
package main
import "fmt"
func main() {
fmt.Printf("Hello, world!\n")
}
不过和C语言不同的是,Go的main
函数不能带有参数,接收命令行参数需要使用os.Args
变长数组。
package main
import (
"fmt"
"os"
)
func main() {
for _, arg := range os.Args {
fmt.Printf("%v\n", arg)
}
}
Go语言中声明函数的格式如下。
func function_name( [parameter list] ) [return_types] {
// 函数体
}
函数传递参数时,要注意值传递和引用传递,这和C语言类似。Go语言中,引用传递使用指针实现,变长数组、字典等本身就是引用类型,因此可以不用指针。下面代码中,i
是值传递,j
则是引用传递,其真正的值被foo
函数修改了。
package main
import "fmt"
func foo(i int, j *int) {
i += 1
*j += 1
}
func main() {
var i, j int = 0, 0
foo(i, &j)
fmt.Printf("%v %v\n", i, j)
}
函数返回值的用法和其他语言都类似,不过这里要注意的是,类似Python语言,Go语言的函数可以有多个返回值,用法例子如下。
func foo() (int, string) {
return 0, "hello"
}
读取其返回值时,我们可以使用如下写法。
var i int
var s string
i, s = foo()
也可以简写为自动类型推导的声明和赋值。
i, s := foo()
我们不关心的类型可以将其忽略。
i, _ := foo()
类似C语言的函数指针,Go语言中函数也可以作为一个变量使用。
假设我们有这样一个函数:
func increment(i int) int {
return i + 1
}
那么我们其实已经可以用increment
进行赋值、参数传递等操作。下面代码我们将increment
赋值给了变量fn
。
var fn func(i int) int = increment
注意这里定义的fn
的类型为func(i int) int
,对应函数声明的参数列表和返回值。当然这一串写起来可能比较复杂,这里我们可以使用类型推导简写,这里就不多说了。
函数指针可以很容易的实现回调函数,下面例子代码中,我们的foo
函数需要传入一个函数指针作为参数,foo
函数执行最后会将其调用。
package main
import "fmt"
func foo(fn func()) {
fmt.Printf("Processing data...\n")
fn()
}
func callback() {
fmt.Printf("Success!\n")
}
func main() {
foo(callback)
}
其实上面代码中的callback
函数可以使用匿名函数简化,例子代码如下。
package main
import "fmt"
func foo(fn func()) {
fmt.Printf("Processing data...\n")
fn()
}
func main() {
foo(func() {
fmt.Printf("Success!\n")
})
}
此外,匿名函数也可以作为返回值,这在下面介绍的闭包中很常用。
匿名函数可以直接访问上一层函数内部的变量,因此可以实现闭包,下面例子代码中我们使用闭包实现输出一个自增的变量。
package main
import "fmt"
func next() func() int {
var i int = 0
return func() int {
i += 1
return i
}
}
func main() {
fn := next()
fmt.Printf("%v\n", fn())
fmt.Printf("%v\n", fn())
fmt.Printf("%v\n", fn())
}
这里注意我们是先使用变量fn
接收了next()
函数的返回值,然后每次调用fn()
函数,这里不要搞错写成每次执行next()()
,这种写法每次next()
返回的都是新的函数。