6 函数
6.1 介绍
Go里面有三种类型的函数:
1.普通的带有名字的函数
2.匿名函数或lambda函数
3.方法(Methods)
除了main()、init()函数外,其他所有类型的函数都可以有参数与返回值。函数参数,返回值和它们的类型统称为函数签名。
注意:Go里面不允许函数重载(编写多个同名函数,它们拥有不同的形参或不同的返回值),会导致编译错误。不支持的原因是:函数重载需要进行多余的类型匹配影响性能。
函数也可以 以声明的形式被使用,作为一个函数类型
type f func(int,int) int
package main
import "fmt"
func add(a, b int) int {
return a + b
}
//sub作为函数名可以看成是 op 类型的常量
func sub(a, b int) int {
return a - b
//定义函数类型 op
type op func(a, b int) int
//形参指定传入参数为函数类型op
func Oper(fu op, a, b int) int {
return fu(a, b)
}
func main() {
//在go语言中函数名可以看做是函数类型的常量,所以我们可以直接将函数名作为参数传入的函数中。
aa := Oper(add, 1, 2)
fmt.Println(aa)
bb := Oper(sub, 1, 2)
fmt.Println(bb)
}
函数可以赋值给变量 例如func Oper(fu op,a ,b int) int。fu变量的类型是op,op是函数类型。
函数不能再其它函数里面声明,不过可以通过匿名函数实现。
6.2 函数参数与返回值
Go默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行修改,但不会影响原来的变量。
如果想要函数可以直接修改参数的值,而不是对参数的副本进行操作,需要将参数的地址(变量名前面添加&)传递给函数,这就是按引用传递,此时传递给函数的是一个指针,指针的值(变量的内存地址)会被复制,但指针指向的变量不会被复制,通过地址来修改变量的值。几乎再所有情况下,传递指针的消耗都比传递副本来的少。
命名返回值和非命名返回值,return在函数中的使用
package main
import "fmt"
var num int = 10
var numx2, numx3 int
func main() {
numx2, numx3 = getX2AndX3(num)
PrintValues()
numx2, numx3 = getX2AndX3_2(num)
PrintValues()
}
func PrintValues() {
fmt.Printf("num = %d, 2x num = %d, 3x num = %d\n", num, numx2, numx3)
}
//getX2AndX3函数只定义返回值的类型,没有定义返回值的名称 这就是非命名返回值
func getX2AndX3(input int) (int, int) {
return 2 * input, 3 * input
}
//getX2AndX3_2函数 既定义了返回值类型也定义了返回值的名称 这是命名返回值
func getX2AndX3_2(input int) (x2 int, x3 int) {
x2 = 2 * input
x3 = 3 * input
// return x2, x3
return
}
return和return var1,var2都可以。
即使函数使用了命名返回值,也可以无视它返回明确的值
不建议定义函数的返回值时 使用非命名返回值
6.2.1 空白符
空白符用来匹配一些不需要的值,然后丢弃掉。
package main
import "fmt"
func main() {
var i1 int
var f1 float32
i1, _, f1 = ThreeValues()
fmt.Printf("The int: %d, the float: %f \n", i1, f1)
}
func ThreeValues() (int, int, float32) {
return 5, 6, 7.5
}
//The int: 5, the float: 7.500000
6.2.2改变外部变量
func main(){
n := 0
reply := &n
loop12(10, 2, reply)
fmt.Println("a * b = ", *reply)
}
func loop12(a, b int, reply *int) {
*reply = a * b
}
//a * b = 20
6.3 传递变长参数
如果函数的最后一个参数是…type的形式,那么这个函数就可以处理一个变长的参数,这个长度可以是0,这样的函数称为变长函数。
func f(a,b, arg ...int) {}
例子1:
func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")
在Greeting函数中,变量who的值为[]string{“Joe”, “Anna”, “Eileen”}
例子2
package main
import "fmt"
func main() {
x := min(1, 3, 2, 5)
fmt.Printf("min num : %d\n", x)
slice := []int{1232, 543, 513, 865, 834, 34}
x = min(slice...)
fmt.Printf("The minimum in the slice is : %d\n", x)
}
func min(s ...int) int {
if len(s) == 0 {
return 0
}
min := s[0]
for _, v := range s {
if v < min {
min = v
}
}
return min
}
//min num : 1
//The minimum in the slice is : 34
如果参数被存储在一个slice类型的变量中,则可以通过slice…的形式来传递参数。
6.4 defer和追踪
使用关键字defer,指定某个语句在函数返回之前(执行return语句之后)才执行。
例子:在f1的函数体执行输出1和3之后 才执行f2函数
func main() {
f1()
}
func f1() {
fmt.Println(1)
defer f2()
fmt.Println(3)
}
func f2() {
fmt.Println(2)
}
//1
//3
//2
例子:调用f3() 会输出0
defer关键字标记了fmt.Println(i) 此时的i = 0
当f3()执行完return 后 再执行fmt.println(i)
func main() {
f3()
}
func f3() {
i := 0
defer fmt.Println(i)
i++
return
}
//0
当一个函数体中需要执行多次defer标记的语句 它们会以逆序执行(先进后出)
func f4() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
//4
//3
//3
//1
//0
6.5 内置函数
Go语言有一些不需要进行导入操作就可以使用的内置函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TJEy5g21-1651279806225)(D:\markdown文件\go\photo\Snipaste_2022-04-25_15-57-56.png)]
6.6 递归函数
func main() {
result := 0
for i := 0; i <= 10; i++ {
result = fibonacci(i)
fmt.Printf("fibonacci(%d) is: %d\n", i, result)
}
hh(10)
}
//根据传入参数n 返回第n个斐波那契数字
func fibonacci(n int) (res int) {
if n <= 1 {
res = 1
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
return
}
//打印10 - 1
func hh(n int) {
if n >= 1 {
println(n)
n--
hh(n)
}
}
fibonacci(0) is: 1
fibonacci(1) is: 1
fibonacci(2) is: 2
fibonacci(3) is: 3
fibonacci(4) is: 5
fibonacci(5) is: 8
fibonacci(6) is: 13
fibonacci(7) is: 21
fibonacci(8) is: 34
fibonacci(9) is: 55
fibonacci(10) is: 89
10
9
8
7
6
5
4
3
2
1
6.7 将函数作为参数
package main
import (
"fmt"
)
type ff func(a, b int) (res int)
func main() {
num1 := mm(add, 10, 5)
num2 := mm(subtract, 10, 5)
fmt.Printf("%d\n", num1)
fmt.Printf("%d\n", num2)
}
func mm(x ff, a, b int) (res int) {
res = x(a, b)
return res
}
func add(a, b int) (res int) {
res = a + b
return
}
func subtract(a, b int) (res int) {
res = a - b
return
}
15
5
6.8 闭包
匿名函数被称为闭包。
当不想给函数起名时,可以使用匿名函数.
这样的函数不能独立存在,将其赋值于某个变量。即保存函数的地址于变量中,然后通过变量名都函数进行调用。
例如
c := func(a, b int) (res int) {
res = a + b
return
}
fmt.Printf("%d\n", c(10, 15))//25
也可以直接对匿名函数进行调用(最后一对括号表示对该匿名函数的调用)
func(a, b int) {
fmt.Printf("%d\n", a*b)
}(10, 2)
//20
6.9 应用闭包:将函数作为返回值
package main
import (
"fmt"
"strings"
)
func f() (ret int) {
defer func() {
ret++
}()
return 1
}
func main() {
fmt.Println(f())
x := ll1()
fmt.Println(x(1))
y := ll2(3)
fmt.Println(y(2))
z := ll3()
fmt.Println(z(1))
fmt.Println(z(10))
fmt.Println(z(100))
addBmp := MakeAddSuffix(".bmp")
addJpeg := MakeAddSuffix(".jpeg")
fmt.Println(addBmp("file"))
fmt.Println(addJpeg("file"))
}
func ll1() func(a int) int {
return func(a int) int {
return a + 1
}
}
func ll2(b int) func(a int) int {
return func(a int) int {
return a + b
}
}
func ll3() func(int) int {
var x int
return func(c int) int {
x += c
return x
}
}
func MakeAddSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
2
2
5
1
11
111
file.bmp
file.jpeg
6.10 计算函数执行时间
使用time包中的Now()和sub函数
func main() {
start := time.Now()//获取当前时间
lll1()
end := time.Now()//获取函数执行完成后时间
delta := end.Sub(start)//执行时间为 end - start
fmt.Printf("runtime use time is :%s\n", delta)
}
func lll1() {
for i := 0; i <= 1000; i++ {
fmt.Printf("%d\n", i)
}
}
//runtime use time is :62.7317ms