函数我们一直都在使用,但是还是有很多细节的内容需要深入了解一下,方便后续的编码实践。
函数定义:
func 函数名([参数列表]) [返回值] {
函数体
}
func add(a int, b int) int {
return a + b
}
基本概念:
- 函数签名(Function Signature):
包含函数名、参数列表(参数的类型和顺序)以及返回值类型。在示例中add(a int, b int) int
就是函数签名。它定义了函数的外部接口,让调用者知道如何正确调用该函数。add
是函数名,(a int, b int)
是参数列表,表明函数接受两个int
类型的参数,最后的int
表示函数返回一个int
类型的值。
- 形参(Formal Parameter):
函数定义中声明的参数,在函数体内部使用,用来接收调用函数时传入的值。这里的a
和b
就是形参,它们是函数体中参与运算的变量占位符。
- 实参(Actual Parameter):
调用函数时传递给函数的实际值。在调用add
函数时传入的3
和5
就是实参,它们会按照顺序传递给对应的形参a
和b
。
- 函数体:
包含实现函数功能的具体代码。在这个例子中,先将 a
和 b
相加的结果赋值给 result
,然后返回 result
。函数体实现了函数具体要执行的操作
- 返回值(Return Value):
函数执行完成后返回给调用者的值。在add
函数中,a + b
的计算结果就是返回值,调用者可以获取这个值进行后续操作。
函数常见用法
作为方法被调用:
调用导出的方法:
例如,我们经常hi用的 print 族方法,被实现在 fmt 包里面。
package main
import "fmt"
func main() {
fmt.Println("调用 fmt 中的 Println()")
}
调用内部的方法:
例如, 在 main 包内部实现一个 add 方法,在main函数里被调用。
package main
import "fmt"
func add(a...int) int {
sum := 0
for _, v := range a {
sum += v
}
return sum
}
func main() {
sum := add(1,2,3,4,5,6,7,8,9,10)
fmt.Println("计算1-10的累加和 : ", sum)
}
这里还涉及到了函数的变参,就不再单独介绍了。这一点也很重要用法
重点需要注意:
这里的入参的类型值得注意,是一个切片
变参要放到最后一个参数
变参中 interface{} 的使用,可以表示任意类型
多返回值的使用:
Go 语言支持函数返回多个值,常见的使用方式是返回结果(一个或多个值)加上错误信息。
package main
import "fmt"
func div(a, b int) (v int, err string) {
if b == 0 {
err = "attempt to divide integer by 0"
return
}
v = a / b
return
}
func main() {
a := 10
b := 0
result, err := div(a, b)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%d/%d = %d\n", a, b, result)
}
a = 10
b = 5
result, err := div(a, b)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%d/%d = %d\n", a, b, result)
}
}
值得注意的是,我这个例子里,我给返回值命名了,所以在函数退出的时候,我就直接写了一个 return 语句。如果没有命名的化,请看下面的例子。
package main
import "fmt"
func div(a, b int) (int, string) {
if b == 0 {
return 0, "attempt to divide integer by 0"
}
return a/b, nil
}
func main() {
a := 10
b := 0
result, err := div(a, b)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%d/%d = %d\n", a, b, result)
}
a = 10
b = 5
result, err := div(a, b)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%d/%d = %d\n", a, b, result)
}
}
函数可作为参数或返回值:
这一点也很容易理解,既然可以作为参数亦可以作为返回值,所以就用一个例子介绍一下如何使用。
package main
import (
"fmt"
"math/rand"
"time"
)
// partition函数用于将切片按照基准元素进行划分
func partition(v []int, left, right int) int {
// 随机化选择基准元素
pivotIndex := left + rand.Intn(right - left + 1)
v[pivotIndex], v[right] = v[right], v[pivotIndex]
pivot := v[right]
i := left - 1
for j := left; j < right; j++ {
if v[j] <= pivot {
i++
v[i], v[j] = v[j], v[i]
}
}
v[i + 1], v[right] = v[right], v[i + 1]
return i + 1
}
func qsort(v []int, left, right int) {
if left < right {
index := partition(v, left, right)
qsort(v, left, index - 1)
qsort(v, index + 1, right)
}
}
func create_qsort_func(f func ([]int, int, int) int) func([]int) {
quickSort := func(v []int) {
left := 0
right := len(v) - 1
if left < right {
index := f(v, left, right)
qsort(v, left, index - 1)
qsort(v, index + 1, right)
}
return quickSort
}
func main() {
rand.Seed(time.Now().UnixNano())
nums := []int{3, 6, 8, 10, 1, 2, 1}
qsort(nums, 0, len(nums) - 1)
fmt.Println(nums)
quickSort := create_qsort_func(partition)
v := []int{3,9,8,4,2,6,7,1,2,5}
quickSort(v)
fmt.Println(v)
}
这个示例里面也涵盖了好几种函数的使用示例:
1. 匿名函数: 在 create_qsort_func 函数中,我创建了一个匿名函数
2. 返回值是函数:在 create_qsort_func 函数中,最后将匿名函数返回
3. 函数作为参数:在 create_qsort_func 函数中,入参是一个函数 partition