• 一文熟悉 Go 函数



    哈喽大家好,我是陈明勇,今天分享的知识是 Go 的函数。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!

    耐心和持久胜过激烈和狂热。

    函数

    函数的英文单词是 Function,这个单词还有着功能的意思。在 Go 语言中,函数是实现某一特定功能的代码块。函数代表着某个功能,可以在同一个地方多次使用,也可以在不同地方使用。因此使用函数,可以提高代码的复用性,减少代码的冗余。

    函数的声明

    通过案例了解函数的声明有哪几部分:
    定义一个函数,实现两个数相加的功能,并将相加之后的结果返回。

    func Add(num1 int, num2 int) int {
    	var sum int
    	sum += num1
    	sum += num2
    	return sum
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过案例可以发现,函数的声明有 5 部分:

    • 1、关键字
      函数的关键字是 func,声明函数必须以 func 关键字开始。
    • 2、函数名
      Go 推荐使用驼峰命名的方式,和变量的命名规则一样,首字母大写的函数名可以在包外访问,小写的只能在包内访问。
    • 3、参数列表
      参数列表中声明了在函数体里所使用到的变量。参数列表位于函数名后面,用括号包裹着,多个参数使用逗号分隔开。
    • 4、返回值列表
      返回值为函数执行后的一个结果,上述代码只有一个返回值,如果有多个返回值,需要用括号包裹着,返回值之间用逗号分隔开。
      少数情况下,我们会声明返回值的的名称如 func Add(num1 int, num2 int) sum int {},多数情况下是不用声明返回值的名称的。
    • 5、函数体
      大括号内就是函数体,存放着函数的具体实现。
      函数的第 34 部分可有可无,也就是说一个函数可以没有参数和返回值。

    Go 函数支持变长参数

    在上面的案例中,实现的功能是对两数求和,如果我们需要对多个数求和,但又不知道具体的数量,函数的参数该怎么声明呢?这时可以声明变长参数去接收多个参数。

    func main() {
    	sum := Add(1, 2, 3, 4)
    	println(sum) // 10
    }
    
    func Add(nums ...int) int {
    	var sum int
    	for _, num := range nums {
    		sum += num
    	}
    	return sum
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    变长参数作为形式参数可以接收不定的实际参数,声明变长参数需要在类型面前加上 ...可变参数实际上是一个切片,可以通过 for-range 去操作。

    匿名函数

    通常情况下,如果一个函数只使用一次,我们就可以定义成匿名函数。

    func main() {
    	// 定义匿名函数,直接调用
    	result := func(num1, num2 int) int {
    		return num1 + num2
    	}(1, 2)
    	println(result)
    	// 2、将匿名函数赋值给一个变量,由变量调用
    	resultFunc := func(num1, num2 int) int {
    		return num1 + num2
    	}
    	println(resultFunc(1, 2))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    声明函数时,不指定函数名的函数,叫做匿名函数。匿名函数可以直接调用,也可以通过赋值给变量,由变量调用。

    闭包

    闭包就是一个函数和其相关引用环境组合的一个整体。

    import "fmt"
    
    // 返回值为一个匿名函数
    func getSum() func(int) int {
    	var sum int = 0
    	// 匿名函数
    	result := func(num int) int {
    		sum += num
    		return sum
    	}
    	return result
    }
    
    func main() {
    	f := getSum()
    	fmt.Println(f(1)) // 1
    	fmt.Println(f(2)) // 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 闭包的本质就是一个匿名函数,匿名函数里使用了定义它的函数(getSum)里面的变量 sum,就组成了闭包。
    • 由上述代码可知,匿名函数中所引用的变量 sum 会一直保存在内存中。

    init 函数

    每个 go 文件都可以包含一个 init 函数,它是一个初始化函数,用于进行初始化的操作。

    var print = getNum()
    
    func getNum() int {
    	println("初始化变量")
    	return 1
    }
    
    func main() {
    	println("main...")
    }
    
    func init() {
    	println("init...")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    执行结果:

    初始化变量
    init...
    main...
    
    • 1
    • 2
    • 3
    • 根据执行结果可知,它们的执行顺序为:全局变量 → init 函数 → main 函数。
    • 多个 go 文件都有 init 函数时,执行顺序为:先执行所导入的模块的 init 函数,再执行本文件的 init 函数。

    函数参数详解

    形式参数与实际参数

    在函数声明的地方,参数列表中的参数被称为形式参数,简称形参;而在函数调用的时候所传递的参数被称为实际参数,简称实参。举例说明:

    func main() {
    	sum := Add(1, 2)
    	println(sum) // 3
    }
    func Add(num1 int, num2 int) int {
    	var sum int
    	sum += num1
    	sum += num2
    	return sum
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Add 后面的参数被称为形参,而在 main 方法里,12 被称为实参。

    值传递

    基本数据类型和数组作为实参时,默认是按 值传递,即进行值拷贝,在函数内修改它们的值,原来的值不会改变。举例说明:

    func main() {
    	num1, num2 := 1, 2
    	Swap(1, 2)
    	fmt.Printf("main 函数体内打印:num1: %d, num2: %d", num1, num2)
    }
    
    func Swap(num1 int, num2 int) {
    	num2, num1 = num1, num2
    	fmt.Printf("Swap 函数体内打印:num1: %d, num2: %d\n", num1, num2)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    执行结果:

    Swap 函数体内打印:num1: 2, num2: 1
    main 函数体内打印:num1: 1, num2: 2
    
    • 1
    • 2

    Swap 函数内,num1num2 的值已经相互交换,但是在 main 函数里,num1num2 的值没有发生变化。

    func main() {
    	nums := [3]int{0, 1, 2}
    	Swap(nums)
    	fmt.Println("main 函数体内打印:nums: ", nums)
    }
    
    func Swap(nums [3]int) {
    	nums[0] = 1
    	fmt.Println("Swap 函数体内打印:nums: ", nums)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    同样传递数组,在函数内修改数组的值,原数组的值并不会改变。
    前面的文章有对指针进行了介绍,指出了通过 * 操作符,可以对指针所指向的变量的值进行修改,因此如果我们想要在函数内改变外部传过来的参数的值,函数声明时,形参类型指定为指针类型。

    func main() {
    	num1, num2 := 1, 2
    	Swap(&num1, &num2)
    	fmt.Printf("main 函数体内打印:num1: %d, num2: %d", num1, num2)
    }
    
    func Swap(num1 *int, num2 *int) {
    	*num2, *num1 = *num1, *num2
    	fmt.Printf("Swap 函数体内打印:num1: %d, num2: %d\n", *num1, *num2)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    执行结果:

    Swap 函数体内打印:num1: 2, num2: 1
    main 函数体内打印:num1: 2, num2: 1
    
    • 1
    • 2

    通过结果可知,使用指针变量作为形参,在函数里是可以改变外部的参数的值的。

    函数是一种数据类型

    Go 里面,函数是一种数据类型,因此函数还可以有很多用法,如创建一个函数变量、将函数作为函数的形参,将函数作为函数的返回值等。

    • 创建函数变量
      func main() {
      	// 创建函数变量
      	hl := Hello
      	fmt.Printf("%T\n", hl) // func(string)
      	hl("cmy")
      }
      
      func Hello(name string) {
      	fmt.Printf("Hello %s", name)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      创建一个函数变量 hl,打印 hl 的类型 → func(string),通过 hl 变量调用函数。
    • 作为函数的形参
      import "fmt"
      
      func main() {
      	Print("cmy", Hello)
      }
      
      func Hello(name string) {
      	fmt.Printf("Hello %s", name)
      }
      
      func Print(name string, f func(string)) {
      	f(name)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      定义函数 Print ,声明两个参数,一个参数是 name,为 string 类型的,另一个参数是 f,为函数类型。传入 cmyHello 函数,由 Print 函数去执行 Hello 函数。
    • 作为函数的返回值
      import "fmt"
      
      func main() {
      	f := Print()
      	f()
      }
      
      func Print() func() {
      	return func() {
      		fmt.Println("Hello,World!")
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      通过Print 函数返回一个匿名函数函数,这个匿名函数的功能是输出 Hello,World!,使用 f 变量接收这个函数并调用。
    • 基于函数自定义类型
      type AddHandleFunc func(num1, num2 int) int
      
      func main() {
      	sum := GetSum(1, 2, Add)
      	println(sum)
      }
      
      func Add(num1, num2 int) int {
      	return num1 + num2
      }
      
      func GetSum(num1, num2 int, handleFunc AddHandleFunc) int {
      	return handleFunc(num1, num2)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      基于函数 func(num1, num2 int) int 自定义一个类型 AddHandleFunc,将它声明为 GetSum 的形参,然后调用 GetSum 时,因为 Add 函数和 AddHandleFunc 是等价的,因此可以将 Add 当做实参传进去。

    小结

    本文对函数的声明进行介绍,根据一个案例了解了其组成的 5 部分。然后介绍了其一些特点如支持变长参数、传递参数时,实参按值传递等,最后根据函数在Go中是一种数据类型的特点,说明了其一些特别用法。

  • 相关阅读:
    Pytorch技法:继承Subset类完成自定义数据集拆分
    《C++ primer plus》第10章:对象和类(1)
    JS教程之 什么是 ‘!!‘ ,在 JavaScript 有什么用?
    内网穿透工具
    分类预测|基于黑翅鸢优化最小二乘支持向量机分类预测Matlab程序BKA-LSSVM 多特征输入多类别输出 含基础LSSVM
    VS+Qt+opencascade三维绘图stp/step/igs/stl格式图形读取显示
    ADC噪声系数 —— 一个经常被误解的参数
    1 Getting Started with Database Administration
    二叉树基础知识总结
    哪一款除甲醛产品效果好 家用针对除甲醛的净化器品牌
  • 原文地址:https://blog.csdn.net/weixin_44604586/article/details/128078790