13、函数
- 定义:为完成某功能的程序指令(语句)的集合,称为函数
- 在go中,函数分为:自定义函数、系统函数
基本语法
func 函数名(形参列表) (返回值列表){ 执行语句 return 返回值列表 }
包的概念和使用
包的本质实际是创建不同的文件夹来管理程序 go的每一个文件都是必须属于一个包 包里的函数名首字母大写才可以被其他包调用 注意细节: 1)包名一般和文件夹名相同,尤其是有IDE插件时 2)使用其他包的函数或变量时,需要先引入
3)package在第一行,然后才是import 4)import时,路径从$GOPATH的src开始查找 5)包可以起别名,替换原有的包名 6)同一个包里函数名、全局变量名不能重复 7)如果要编译,需要有一个包声明为main: go
build l6/d1/main -o xxx.exe,编译过程中会生成库文件
函数的调用
多个返回值时,如果有某个不需要,可以用 _ 接收 返回值只有一个时,返回列表不需要()
函数的递归调用
一个函数在函数体内又调用了本身,称为递归调用 注意:递归必须向退出条件逼近,否则就是无限循环
函数的形参列表中使用可变参数时,需要放在形参的最后位置 args
init函数:构建函数,在main()函数前调用 如果一个文件同时包含全局变量定义、init、main函数,则执行顺序是:全局变量定义->init->main 如果包里和main.go都有init,引入后执行的顺序是:包里的变量定义->
包里的init->main的变量定义->main的init->main()
package main
import (
"fmt"
"code/utils"
)
func getSumAndSub(n1 int, n2 int) (int, int) {
sum := n1 + n2
sub := n1 - n2
return sum, sub
}
func test(n int) {
if n > 2 {
n--
test(n)
}
fmt.Printf("n=%v\n", n)
}
func test2(n int) {
if n > 2 {
n--
test2(n)
} else {
fmt.Printf("n=%v\n", n)
}
}
//斐波那契数列
//当n==1||n==2时,返回1
//当n>2,返回前面2个数的和 f(n-1)+f(n-2)
func fbn(n int) int {
if n == 1 || n == 2 {
return 1
} else {
return fbn(n-1) + fbn(n-2)
}
}
//可变参数的使用
func sum(n1 int, args ...int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
func swap(n1 *int, n2 *int) {
t := *n1
*n1 = *n2
*n2 = t
}
func main() {
// a := utils.calc(11.2, 42.1, '+')
b := utils.Calc2(3.2, 1.3, '/')
fmt.Printf("结果是%.2f\n", b) //格式化输出小数点后2位
res1, res2 := getSumAndSub(3, 5)
fmt.Printf("两个数的和是%v,差是%v\n", res1, res2)
res3, _ := getSumAndSub(4, 6)
fmt.Printf("两个数的和是%v\n", res3)
test(4) //test(4) test(3) test(2) 输出3次
test2(4) //test2(2)走到else里,输出1次
for i := 1; i <= 20; i++ {
m := fbn(i)
fmt.Printf("%v,", m)
}
res4 := sum(1, 2, 3, 4, 5)
fmt.Println("res4=", res4)
x := 10
y := 20
swap(&x, &y)
fmt.Printf("x=%v,y=%v", x, y)
}
package utils
import "fmt"
//小写字母开头的函数,不能被其他包调用
func calc(n1 float64, n2 float64, operator byte) float64 {
var res float64
switch operator {
case '+':
res = n1 + n2
case '-':
res = n1 - n2
case '*':
res = n1 * n2
case '/':
res = n1 / n2
default:
fmt.Println("操作符错误")
}
return res
}
//大写字母开头,可以调用
func Calc2(n1 float64, n2 float64, operator byte) float64 {
var res float64
switch operator {
case '+':
res = n1 + n2
case '-':
res = n1 - n2
case '*':
res = n1 * n2
case '/':
res = n1 / n2
default:
fmt.Println("操作符错误")
}
return res
}
匿名函数:没有命名的函数,如果某个函数只是希望使用一次,可以使用匿名函数
可以定义时直接调用、赋值给一个变量 如果将匿名函数赋值给全局变量,那么该函数成为全局匿名函数
package main
import "fmt"
var Fun1 = func(n1 int, n2 int) int {
return n1 * n2
}
func main() {
//匿名函数
res := func(n1 int, n2 int) int {
return n1 + n2
}(10, 2)
fmt.Printf("res=%v\n", res)
//将匿名函数赋值给变量a
a := func(n1 int, n2 int) int {
return n1 - n2
}
fmt.Printf("a=%v\n", a(20, 3))
fmt.Printf("f=%v\n", Fun1(3, 4))
}
闭包:一个函数与其相关的引用环境组合的一个实体
package main
import (
"fmt"
"strings"
)
//累加器
//addupper返回的数据类型是func(int)int匿名函数,引用到了函数外的变量n,这组成了一个闭包
func AddUpper() func(int) int {
var n int = 10
var str = "a"
return func(x int) int {
n = n + x
str += "x"
fmt.Println("str=", str)
return n
}
}
func exec1() {
f := AddUpper()
//当我们重复调用AddUpper函数时,因为n是初始化一次,因此每次结果会累加,而不会重新初始化n
//关键一点是需要分析出函数使用到哪些变量
fmt.Printf("addupper=%v\n", f(1))
fmt.Printf("addupper=%v\n", f(1))
fmt.Printf("addupper=%v\n", f(1))
}
//接收一个文件后缀名,并返回一个闭包
//调用闭包,可以传入一个文件名,如果没有指定后缀,则加上,如果有,则返回文件名
func makeSuffix(suffix string) func(string) string {
//返回的匿名函数和suffix变量组成闭包
return func(name string) string {
if strings.HasSuffix(name, suffix) == false {
return name + suffix
}
return name
}
}
//闭包对比一般函数的写法,.jpg只需要传入一次,一般函数需要每次都传入.jpg参数
func makeSuffix2(suffix, name string) string {
//一般函数的写法,也能实现
if strings.HasSuffix(name, suffix) == false {
return name + suffix
}
return name
}
func exec2() {
f := makeSuffix(".jpg")
fmt.Println("file name=", f("jack"))
fmt.Println("file name=", f("jack.jpg"))
fmt.Println("file name=", makeSuffix2(".jpg", "handsome"))
fmt.Println("file name=", makeSuffix2(".jpg", "handsome.jpg"))
}
func main() {
exec2()
}
defer:golang中的延时机制,常用于资源释放,以便于在使用资源时就定义延时释放,以免忘记关闭
package main
import "fmt"
func main() {
//defer的命令延时执行,按先入后出顺序
//defer中的变量值也保存了初始状态
n := 1
defer fmt.Println("defer1", n)
defer fmt.Println("defer2", n)
defer fmt.Println("defer3", n)
n++
fmt.Println("n=", n)
}
函数参数的传递方式
值传递:将值拷贝,数据越大效率越低; 基本数据类型int、float、bool、string、数组、结构体 引用传递:地址的拷贝,效率高;指针、切片、map、管道、接口等
如果希望函数内修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量
变量的作用域
函数内部声明/定义的变量,作用域仅限于函数内部 赋值语句不能在函数外执行,全局变量 Name := “jack” 会编译报错
package main
import (
"fmt"
"funcinit/utils"
)
var tmp = test()
func test() int {
fmt.Println("test()")
return 90
}
func init() {
fmt.Println("init()...")
}
func main() {
fmt.Printf("main()...tmp=%v\n", tmp)
fmt.Printf("age=%v,name=%v", utils.Age, utils.Name)
}
package utils
import "fmt"
var Age int
var Name string
//Age和Name需要在main.go中使用
//但是需要初始化
//通过init函数完成初始化
func init() {
fmt.Println("init包")
Age = 100
Name = "jack"
}