函数
基本介绍
为完成某一功能的程序指令(语句)的集合,称为函数。在 Go 中,函数分为: 自定义函数、系统函数
基本语法
基本语法
使用案例
package main
import "fmt"
func main() {
result := getResult(5.2, 5.3, '+')
fmt.Println("result=", result)
}
func getResult(num1 float64, num2 float64, byte byte) float64 {
var result float64
switch byte {
case '+':
result = num1 + num2
case '-':
result = num1 - num2
case '*':
result = num1 * num2
case '/':
result = num1 / num2
}
return result
}
包
基本介绍
go 的每一个文件都是属于一个包的,也就是说 go 是以包的形式来管理文件和项目目录结构的。
包的本质实际上就是创建不同的文件夹,来存放程序文件。
包的作用
区分相同名字的函数、变量等标识符
当程序文件很多时,可以很好的管理项目
控制函数、变量等访问范围,即作用域
基本语法
打包的基本语法
package 包名
引入包的基本语法
import "包的路径"
使用案例
将getResult()封装进util.go,并在main.go中进行调用
main.go
package main
import (
"day08/utils"
"fmt"
)
func main() {
result := utils.GetResult(5.2, 5.3, '+')
fmt.Println("result=", result)
}
util.go1
package utils
func GetResult(num1 float64, num2 float64, byte byte) float64 {
var result float64
switch byte {
case '+':
result = num1 + num2
case '-':
result = num1 - num2
case '*':
result = num1 * num2
case '/':
result = num1 / num2
}
return result
}
目录介绍
注意事项
在给一个文件打包时,该包对应一个文件夹,比如这里的 utils 文件夹对应的包名就是 utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
当一个文件要使用其它包函数或变量时,需要先引入对应的包
package 指令在 文件第一行,然后是 import 指令。
在 import 包时,路径从 $GOPATH 的 src 下开始,不用带 src , 编译器会自动从 src 下开始引入
为了让其它包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其它语言的 public ,这样才能跨包访问。
在访问其它包函数,变量时,其语法是 包名.函数名
如果包名较长,Go 支持给包取别名, 注意细节:取别名后,原来的包名就不能使用了。如果给包取了别名,则需要使用别名来访问该包的函数和变量。
在同一包下,不能有相同的函数名(也不能有相同的全局变量名),否则报重复定义
如果你要编译成一个可执行程序文件,就需要将这个包声明为 main , 即 package main .这个就是一个语法规范,如果你是写一个库 ,包名可以自定义
util.go编译会在pkg文件夹内生成一个库文件,以.a结尾
package main
import (
test "day08/utils"
"fmt"
)
func main() {
// 取别名
result := test.GetResult(5.2, 5.3, '+')
fmt.Println("result=", result)
}
函数的调用机制
使用案例1
案例一说明
在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其它的栈的空间区分开来
在每个函数对应的栈中,数据空间是独立的,不会混淆
当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间。
使用案例2
计算两个数,并返回
package main
import "fmt"
func testSum(num1 int, num2 int) int {
return num1 + num2
}
func main() {
sum := testSum(1, 2)
fmt.Println("sum=", sum)
}
return语句
基本语法
基本语法
注意事项
返回多个值的时候,希望忽略某个返回值,则使用 _ 符号表示占位忽略
package main
import "fmt"
func getSumAndSub(num1 int, num2 int) (int, int) {
sum := num1 + num2
sub := num1 - num2
return sum, sub
}
func main() {
sum, sub := getSumAndSub(5, 2)
fmt.Println("sum=", sum, "; sub=", sub)
// 忽略某个返回值
res, _ := getSumAndSub(5, 2)
fmt.Println("res=", res)
}
递归调用
基本介绍
一个函数在函数体内又调用了本身,我们称为递归调用
入门案例
package main
import "fmt"
func test(n int) {
if n > 2 {
n--
test(n)
}
fmt.Println("n=", n)
}
func main() {
test(4)
}
分析
总结
执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
函数的局部变量是独立的,不会相互影响
递归必须向退出递归的条件逼近,否则就是无限递归,死归了
当一个函数执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁
练习1
求函数值,已知 f(1)=3; f(n) = 2*f(n-1)+1;请使用递归的思想编程,求出 f(n)的值?
package main
import "fmt"
func test1(n int) int {
if n == 1 {
return 3
} else {
return 2*test1(n-1) + 1
}
}
func main() {
fmt.Println("f(1)=", test1(1))
fmt.Println("f(3)=", test1(3))
}
练习2
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
package main
import "fmt"
func peach(n int) int {
if n > 10 || n < 1 {
return 0
}
if n == 10 {
return 1
} else {
return (peach(n+1) + 1) * 2
}
}
func main() {
fmt.Println("peach1=", peach(1))
fmt.Println("peach10=", peach(10))
}
函数的注意事项
函数的形参列表可以是多个,返回值列表也可以是多个。
形参列表和返回值列表的数据类型可以是值类型和引用类型。
函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似 public , 首字母小写,只能被本包文件使用,其它包文件不能使用,类似 privat
函数中的变量是局部的,函数外不生效
基本数据类型和数组默认都是值传递的,即进行值拷贝,在函数内修改,不会影响到原来的值。如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传入变量的地址&,函数内以指针的方式操作变量。
Go 函数不支持函数重载
在 Go 中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用
函数既然是一种数据类型,因此在 Go 中,函数可以作为形参,并且调用
为了简化数据类型定义,Go 支持自定义数据类型
基本语法:type 自定义数据类型名 数据类型 // 理解: 相当于一个别名
支持对函数返回值命名
使用 _ 标识符,忽略返回值
Go 支持可变参数,如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。
package main
import "fmt"
func main() {
var i int = 10
zhichuandi(&i)
fmt.Println("i =", i)
f := test1
n := f(1, 3) // 等价于n := test1(1, 3)
fmt.Println("n =", n)
fmt.Printf("f的基本数据类型%T, test1的基本数据类型%T \n", f, test1)
testN := test2(test1, 3, 4)
fmt.Println("testN =", testN)
//为了简化数据类型定义,Go 支持自定义数据类型
type myInt int
var num1 myInt = 1
var num2 int = int(num1) // 需要进行类型转换
fmt.Println("num1 =", num1, "; num2 =", num2)
num3 := test3(test1, 4, 4)
fmt.Println("test4 =", num3)
sub, sum := getSubAndSum(5, 5)
fmt.Println("sub =", sub, "; sum =", sum)
i2 := test4(1, 3, 4, 5, 6)
fmt.Println("i2 =", i2)
}
// 值传递
func zhichuandi(i *int) {
*i = *i + 10
}
// 在 Go 中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了
func test1(num1 int, num2 int) int {
return num1 + num2
}
// 函数既然是一种数据类型,因此在 Go 中,函数可以作为形参,并且调用
func test2(myfun func(int, int) int, n1 int, n2 int) int {
return myfun(n1, n2)
}
// 为了简化数据类型定义,Go 支持自定义数据类型,对函数取别名
type myfuncType func(int, int) int
func test3(myfun myfuncType, n1 int, n2 int) int {
return myfun(n1, n2)
}
// 支持对函数返回值命名
func getSubAndSum(n1 int, n2 int) (sub int, sum int) {
sum = n1 + n2
sub = n1 - n2
return
}
// Go 支持可变参数
func test4(num int, val ...int) int {
count := num
for i := 0; i < len(val); i++ {
count += val[i]
}
return count
}
练习
请编写一个函数 swap(n1 int, n2 int) 可以交换 n1 和 n2 的值
package main
import "fmt"
func main() {
n1 := 10
n2 := 20
swap(&n1, &n2)
fmt.Println("n1 =", n1, "; n2 =", n2)
}
func swap(n1 *int, n2 *int) {
*n2 += *n1
*n1 = *n2 - *n1
*n2 = *n2 - *n1
}
评论区