函数

Go语言的函数,很像JavaScript语言的函数

func 函数名称 (形参1, 形参2) (返回值类型列表) {
  函数体
  retrun 返回值列表
}

函数的声明

函数的命名

  • 函数名也是标识符,遵循标识符的命名规范

  • 首字母小写,只能被本包使用

  • 首字母大写,可以被本包以及其他包使用

重载:不支持

// 以下代码会报错
// 编译器会认为下面的两个方法定义是重复的
func test() {}

func test(a string) {}

无参函数

func test() {}

有参函数:参数类型一致

func test(x, y float32) {}

有参函数:类型不一致

func test(x string, y int) {}

有参函数:可变参数

func test(names...int) {
  // 可变参数在方法内会被转换为切片,可以对切片进行遍历取值
}

无返回值函数

如果是无返回函数,返回值类型不填

func test() {
}
// 调用函数
test()

单返回值函数

如果只有一个返回值,可以省略括号

func test() string {
  return "hello"
}
// 调用函数
var s = test()

多返回值函数

函数可以有多个返回值,多个返回值返回使用逗号隔开

func test() (string, int) {
  return "hello", 20
}
// 调用函数
var s, i = test()

带有变量名的返回值

func namedRetValues() (a, b int) {
    a = 1
    b = 2
    return
}
// 直接返回a 和 b

函数的调用

调用多返回值函数

var s, i = test()

忽略多返回值函数返回值

// 调用函数,忽略部分返回值
var s, _ = test()

函数参数的值传递与引用传递

基本类型形参是值传递

// 交换两个数字
func exchangeNum(num1 int, num2 int) {
	var t int
	t = num1
	num1 = num2
	num2 = t
}

func main() {
	var i = 10
	var j = 20
	exchangeNum(i, j)
	fmt.Printf("i = %d, j = %d", i, j)  // 结果为 i = 10, j = 20
}

使用指针可以做到引用传递:

// 交换两个数字
// 参数类型为指针
func exchangeNum(num1Ptr *int, num2Ptr *int) {
	var t int
	t = *num1Ptr
	*num1Ptr = *num2Ptr
	*num2Ptr = t
}

func main() {
	var i = 10
	var j = 20
	exchangeNum(&i, &j)
	fmt.Printf("i = %d, j = %d", i, j) // 结果为 i = 20, j = 10
}

引用类型形参是引用传递

  • 指针

  • 切片

  • map

  • 函数

  • channel

等数据类型。

匿名函数

定义并直接调用匿名函数

func(data int) {
    fmt.Println("hello", data)
}(100)

变量函数

// 将匿名函数体保存到f()中
f := func(data int) {
    fmt.Println("hello", data)
}
// 使用f()调用
f(100)

变量函数传递

package main
import (
    "fmt"
)
// 遍历切片的每个元素, 通过给定函数进行元素访问
func visit(list []int, f func(int)) {
    for _, v := range list {
        f(v)
    }
}
func main() {
    // 使用匿名函数打印切片内容
    visit([]int{1, 2, 3, 4}, func(v int) {
        fmt.Println(v)
    })
}

匿名函数应用技巧

func main() {
	var skill = map[string]func(){
		"fire": func() {
			fmt.Println("chicken fire")
		},
		"run": func() {
			fmt.Println("soldier run")
		},
		"fly": func() {
			fmt.Println("angel fly")
		},
	}
	skill["fire"]()
}

闭包(Closure)

闭包就是能够读取其他函数内部变量的函数,在其他语言中,也称为Lambda表达式。在Golang中,闭包组成如下:

匿名函数 + 引用环境 = 闭包

下面是一个闭包累加器:

package main

import "fmt"

// 定义一个名为getSum的函数,无参,返回值是一个func (int) int 的函数
func getSum() func (int) int {
	var sum = 0
	return func(num int) int { // 这个拥有sum变量的匿名函数就是闭包
		sum = sum + num // sum变量的作用域被扩大,将会被一直保存在内存中,所以要适当使用闭包
		fmt.Println(sum)
		return sum
	}
}

func main() {
	sumF := getSum()
	sumF(1) // 1. 入门
	sumF(2) // 3
	sumF(3) // 6
}

结果:

$ go run main.go 
1
3
6

defer 延迟执行语句

类似于Java的Finally语句,通常用于标记关闭资源的语句,让其使用完毕后自动关闭:

package main
import (
	"fmt"
)
func main() {
	testDefer()
}
func printReturn() int {
	fmt.Println("defer return")
	return 0
}

func testDefer() int {
	fmt.Println("defer begin")
	var num1 int = 30
	var num2 int = 60

	// 将defer放入延迟调用栈,这是一个新的栈空间
	// 涉及的变量如果是基本类型,则会进行值传递,如果是引用类型则是传递引用
  // 因为num1和num2是基本类型,所以打印30和60
	defer fmt.Println(num1) // 先放入的是栈底,最后调用
	defer fmt.Println(num2) // 后放入的是栈顶,最先调用

	num1 += 10
	num2 += 10

	fmt.Println("num1 和 num2:", num1, num2)

	return printReturn() // defer 在return语句之后运行
}

执行结果如下:

$ go run main.go 
defer begin
num1  num2: 40 70
defer return
60
30

最后更新于