函数
目录
一、什么是函数
- 简单来说就是一个特定的方法,通过这个方法产生预期的结果。
- 函数是一块执行特定任务的代码。一个函数是在输入源基础上,通过执行一系列的算法,生成预期的输出
二、函数的定义
1. 定义函数的语法
func 函数名(参数1 参数1类型,参数2 参数2类型) 返回值类型 {
// 函数体(代码块)
}
1.1 若参数类型一致或返回值类型一致,则可简写
func 函数名(参数1,参数2 参数类型) (返回值1,返回值2 返回值类型) {
// 函数体(代码块)
}
1.2 示例,定义一个计算总价的函数
func calculateBill(price int, no int) int {
var totalPrice = price * no // 商品总价 = 商品单价 * 数量
return totalPrice // 返回总价
}
2. *****************函数中的参数列表和返回值并非是必须的,所以下面这个函数的声明也是有效的***************
func test1() {
// 译注: 表示这个函数不需要输入参数,且没有返回值
}
3. 若参数或者返回值都为同一个类型的,则可以简写
func 函数名(参数1,参数2 参数类型...) 返回值类型 {
// 函数体(代码块)
}
三、函数的参数
-
注意:函数的参数传递都是拷贝传递,相当于将参数的值拷贝一份,传入函数,所以要想在函数内部同步修改参数本身,就要用到指针
-
和python不同的是,go中没有位置参数和关键字参数的概念,go中都是按照位置传参
-
只需要熟悉 可变长参数函数
-
函数作为参数
-
func add(x, y int) int { return x + y } func calc(x, y int, op func(int, int) int) int { return op(x, y) } func main() { ret2 := calc(10, 20, add) fmt.Println(ret2) //30 }
-
四、函数的返回值
- 和python不同的是,GO中对函数的返回值的类型也必须提前写死
- 想接收一个函数的返回值,该函数有几个返回值必须要声明几个变量来接收,也可以用匿名变量(或者叫空白符)
_
来接收某个返回值
1. 多个返回值
- go中是支持多个返回值的,返回值类型用括号括起来即可
1. 语法是
func 函数名(参数1 参数1类型,参数2 参数2类型) (返回值1类型,返回值2类型) {
// 函数体(代码块)
return 值1,值2...
}
1.1 示例,定义一个计算矩形的周长和面积的函数
package main
import (
"fmt"
)
func rectProps(length, width float64)(float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, perimeter := rectProps(10.8, 5.6)
fmt.Printf("Area %f Perimeter %f", area, perimeter)
}
2. 命名返回值
- 在定义函数时,若指定了返回值的变量名,则在函数体中,也不用重新声明该返回的变量,不需要再明确指定返回值
1. 若返回值都为同一个类型的,则可以简写
func 函数名(参数1,参数2 参数类型) (返回值1,返回值2 返回值类型) {
// 函数体(代码块)
}
1.1 如下面的rectProps方法
func rectProps(length float64, width float64)(area float64, perimeter float64) {
area = length * width
perimeter = (length + width) * 2
return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}
1.1.1 可以简写为:
func rectProps(length, width float64)(area, perimeter float64) {
area = length * width // 直接赋值,不用声明
perimeter = (length + width) * 2 // 直接赋值,不用声明
return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}
3. 返回值补充
(1)nil
作为返回值时
-
当我们的一个函数返回值类型为切片时,
nil
可以看做是一个有效的,没必要显示返回一个长度为0的切片- 其他空值也为
nil
的如map 、指针等
,也同样适用上述规则
func someFunc(x string) []int { if x == "" { return nil // 没必要返回[]int{} } ... }
- 其他空值也为
(2)函数作为返回值
func do(s string) (func(int, int) int, error) {
switch s {
case "+":
return add, nil
case "-":
return sub, nil
default:
err := errors.New("无法识别的操作符")
return nil, err
}
}
五、匿名函数
- 和python中的匿名函数,差别很大,Go中的匿名函数没有关键字,相当于用变量去存储一个没名字的正常函数,代码格式类似python中的闭包函数
- 同普通函数一样,匿名函数的参数类型和返回值的类型都是该匿名函数类型的一部分
- 匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数
1. 匿名函数的定义
-
匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数
-
加括号可以直接调用
-
下面是没有返回值的闭包函数
语法: 变量名 := func(参数 类型) 返回值类型{函数体}
// 示例:
package main
import (
"fmt"
)
func main() {
// 将匿名函数保存到变量
add := func(x, y int) {
fmt.Println(x + y)
}
add(10, 20) // 通过变量调用匿名函数
//自执行函数:匿名函数定义完加()直接执行
func(x, y int) {
fmt.Println(x + y)
}(10, 20)
}
2. 闭包函数
-
闭包函数指的是一个函数和它引用环境组合在一起,形成的一个组合体,该组合体称为闭包函数。简而言之,
闭包函数=函数+引用环境
-
闭包一定是嵌套函数,但嵌套函数不一定是闭包函数
-
闭包函数的概念就使得其有个特点,即在闭包函数的内部,对外层的作用域有引用。意思就是闭包函数内部使用外层的变量都是使用的地址,而非拷贝的值
package main
import (
"fmt"
)
func test3(a, b int) func(a, b string) (int, int, int) {
fmt.Println("test3 a,b:", a, b)
fmt.Printf("test3 a,b的地址: %p %p\n", &a, &b) // %p 的作用是打印地址
f := func(x, y string) (int, int, int) {
fmt.Println("x,y:", x, y)
fmt.Printf("匿名函数内部 a,b的地址: %p %p\n", &a, &b)
return 3, 4, 5
}
return f
}
func main() {
a, b := 1, 2
println("外部a,b地址:", &a, &b)
f := test3(a, b)
fmt.Println("f:", f)
f("xxx", "yyy")
}
/*
打印结果:
外部a,b地址: 0xc000107f10 0xc000107f08
test3 a,b: 1 2
test3 a,b的地址: 0xc00000a0d8 0xc00000a0f0
f: 0xafb1a0
x,y: xxx yyy
匿名函数内部 a,b的地址: 0xc00000a0d8 0xc00000a0f0
可以看出,test3 a,b的地址 和 匿名函数内部 a,b的地址 是一样的,但它们和test3外部的x,y的地址是不同的
*/
(1)闭包函数简单示例
package main
import (
"fmt"
)
func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder()
fmt.Println(f(10)) //10
fmt.Println(f(20)) //30
fmt.Println(f(30)) //60
f1 := adder()
fmt.Println(f1(40)) //40
fmt.Println(f1(50)) //90
}
// 注意每次的打印结果
(2)闭包函数进阶示例
i. 示例1
package main
import (
"fmt"
)
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder2(10)
fmt.Println(f(10)) //20
fmt.Println(f(20)) //40
fmt.Println(f(30)) //70
f1 := adder2(20)
fmt.Println(f1(40)) //60
fmt.Println(f1(50)) //110
}
// 注意每次的打印结果
ii. 示例2
package main
import (
"fmt"
)
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
}
// 注意每次的打印结果
iii. 示例3
package main
import (
"fmt"
)
func calc(base int) (func(int) int, func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add, sub
}
func main() {
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2)) //11 9
fmt.Println(f1(3), f2(4)) //12 8
fmt.Println(f1(5), f2(6)) //13 7
}
// 注意每次的打印结果