函数
定义一个函数
不支持 嵌套 (nested)、重载 (overload) 和 默认参数 (default parameter)。
- 无需声明原型。(与C不同)
- 支持不定长变参。(参数列表可不定长)
- 支持多返回值。
- 支持命名返回参数。
- 支持匿名函数和闭包。
使用关键字 func
定义函数,左大括号依旧不能另起一行。
func test(x, y int, s string) (int, string) { // 类型相同的相邻参数可合并,多返回值必须用括号。
n := x + y
return n, fmt.Sprintf(s, n)
}
函数是一种数据类型
在Let’s Go 2 — 类型与字符串中的类型列表中提到,函数是一种数据类型(与java不同)
因此,函数可以作为参数,作为变量传递
package main
import "fmt"
func test(fn func() int) int {//函数可以作为变参
return fn()
}
type FormatFunc func(s string, x, y int) string // 定义函数类型。
func format(fn FormatFunc, s string, x, y int) string {
return fn(s, x, y)
}
func main() {
s1 := test(func() int { return 100 }) // 直接将匿名函数当参数。
s2 := format(func(s string, x, y int) string {
return fmt.Sprintf(s, x, y)
}, "%d, %d", 10, 20)
fmt.Println(s1, s2)
}
变参
变参本质上就是 slice
。只能有一个,且必须是最后一个。(和java一样)
func test(s string, n ...int){
//本质上是个slice,可用range遍历
for _,v := range n{
fmt.Println(s,v)
}
}
使用 slice
对象做变参时,必须展开。
package main
import "fmt"
func main() {
test(1,2,3)
s := []int{1,2,3}
//test(s) 报错,必须展开,这一点和java一样
}
func test(n ...int){
for _,v := range n{
fmt.Println(v)
}
}
返回值
不能用容器对象接收多返回值。只能用多个变量,或 "_" 忽略。
func test() (int, int) {
return 1, 2
}
func main() {
// s := make([]int, 2)
// s = test() // Error: multiple-value test() in single-value context
x, _ := test()
println(x)
}
多返回值可直接作为其他函数调用实参。
func test() (int,int){
return 1,2
}
func add(x, y int) int {
return x + y
}
func sum(n ...int) int {
var x int
for _, i := range n {
x += i
}
return x
}
func main() {
//多返回值函数可以直接用作参数
println(add(test()))
println(sum(test()))
}
命名返回参数可看做与形参类似的局部变量,最后由 return
隐式返回。
func add(x, y int) (z int) {
z = x + y
return //返回z
}
func main() {
println(add(1, 2))
}
命名返回参数可被同名局部变量遮蔽,此时需要显式返回。
func add(x, y int) (z int) {
{ // 不能在一个级别,引发 "z redeclared in this block" 错误。
var z = x + y
// return // Error: z is shadowed during return
return z // 必须显式返回。
}
}
命名返回参数允许 defer
延迟调用通过闭包读取和修改。
func add(x, y int) (z int) {
defer func() {
z += 100
}()
z = x + y
return
}
func main() {
println(add(1, 2)) // 输出: 103
}
显式 return
返回前,会先修改命名返回参数。
func add(x, y int) (z int) {
defer func() {
println(z) // 输出: 203
}()
z = x + y
return z + 200 // 执⾏行顺序: (z = x + y) -> (z = z + 200) -> (call defer) -> (return)
}
func main() {
println(add(1, 2)) // 输出: 203
}
匿名函数
匿名函数可赋值给变量,做为结构字段,或者在 channel
里传送。
// --- 函数作为变量 ---
fn := func() { println("Hello, World!") }
fn()
// --- 函数数组 ---
fns := [](func(x int) int){
func(x int) int { return x + 1 },
func(x int) int { return x + 2 },
}
println(fns[0](100))
// --- 函数作为属性 ---
d := struct {
fn func() string
}{
fn: func() string { return "Hello, World!" },
}
println(d.fn())
// --- 函数管道 ---
fc := make(chan func() string, 2)
fc <- func() string { return "Hello, World!" }
println((<-fc)())
延迟引用现象
闭包复制的是原对象指针!!
func main(){
i, n := 1 ,2
// defer:编译仍然从上到下,但会放下面执行
defer func(a int){
fmt.Println("defer:", a , n) //n被闭包引用,引用的是指针,所以后续的修改对此处有影响
}(i) //复制i的值,此时i为1
i , n = i+1,n+2
fmt.Println(i , n)
}
// 输出
// 2 4
// defer: 1 4
参考
Q.E.D.