5、go的函数
函数写法
func main() {
sum := cal(1, 1)
fmt.Println(sum) // 2
}
// 函数名首字母如果大小,可以在其他包中引用
// 函数语法:参数列表(可以0个或多个),返回值类型列表(可以0个或多个)
func cal(num1 int, num2 int) (int) { // 如果只有一个返回值参数,可以不写小括号,直接int就好了
return num1 + num2
}
如果没有返回值,对应的位置不写就行了,例如:
func main() {
cal(1, 1)
}
func cal(num1 int, num2 int) { // 没有返回值,这个位置可以不写
fmt.Println(num1 + num2) // 2
}
函数返回多个结果参数,例如:
func main() {
sum, result := cal2(1, 1)
fmt.Println(sum) // 2
fmt.Println(result) // 0
}
func cal2(num1 int, num2 int) (int, int) { // 返回两个参数,一个是两数之和,一个是两数之差
return num1 + num2, num1 - num2
}
如果只取一个结果,例如;
func main() {
// 如果返回的参数只想要其中一个,使用_ 把另一个给忽略了就行了
sum, _ := cal2(1, 1)
fmt.Println(sum) // 2
}
func cal2(num1 int, num2 int) (int, int) { // 返回两个参数,一个是两数之和,一个是两数之差
return num1 + num2, num1 - num2
}
问题:
我给两个值进行交换,结果发现交换失败
func main() {
var num1 int = 10
var num2 int = 20
fmt.Printf("交换前两个数: num1: %v, num2: %v \n", num1, num2) // 10, 20
exchangeNum(num1, num2)
fmt.Printf("交换后两个数: num1: %v, num2: %v \n", num1, num2) // 10, 20
}
// 给两个参数互相交换值
func exchangeNum(num1 int, num2 int) {
var t int
t = num1
num1 = num2
num2 = t
}
内存分析:
所以基本数据类型和数组默认都是值传递,即值拷贝,在函数内修改,不会影响原来的值
重载问题
go不支持重载,如果有多个同名的函数,尽管参数不同,也会编译报错的
参数数量是可变的,例如:
func main() {
test(3)
test(1, 1, 1)
}
func test(args ...int) {
// 函数内部处理可变参数时候,把可变参数当作切片处理
for i := 0; i < len(args); i++ {
fmt.Println(args[i])
}
}
会打印一个3,然后打印3个1
值传递数据类型参数
func main() {
var num int = 10
fmt.Println(num) // 10
test(&num)
fmt.Println(num) // 30
}
func test(num *int) {
// 指针修改函数传递值
*num = 30
}
内存分析:
数据类型——函数
函数是数据类型,所以可以赋值给一个变量接收
func main() {
// 函数也是一种数据类型, 可以赋值给一个变量
a := test
fmt.Printf("a的类型: %T, test的类型: %T \n", a, test) // a的类型: func(int), test的类型: func(int)
// 通过该变量可以给对应的函数调用赋值
a(10) // 10
}
func test(num int) {
fmt.Println(num)
}
go里面把函数也当成数据类型,所以可以当作参数入参
func main() {
// 调用test02函数
test02(10, 3.14, test) // 最后一个参数传入test函数
test02(10, 3.14, a) // 传入a也可以, 是等价的
}
func test(num int) {
fmt.Println(num)
}
func test02(num1 int, num2 float32, testFunc func(int)) {
fmt.Println("------test02")
testFunc(99) // 这里又调用了test函数
}
为了简化数据类型定义,go支持自定义数据类型
type myInt int
var num myInt = 30
fmt.Println("num: ", num) // 30
var abc int = 10
// 虽然num是myInt类型的,是int的别名,但是myInt和int不是一种数据类型,所以这样会报错
//abc = num
// 如果想要赋值给abc这个int的变量, 可以把myInt类型转为int类型
abc = int(num)
fmt.Println("abc: ", abc) // 30
既然go支持自定义数据类型,那函数也是数据类型,所以也可以给函数给别名
func test(num int) {
fmt.Println(num)
}
// 给上面的test函数给别名
type myFunc func(int)
func test02(num1 int, num2 float32, testFunc myFunc) { // 这里入参使用自函数别名
fmt.Println("------test02")
testFunc(99) // 这里又调用了test函数
}
有的还可以对函数的返回值命名
init函数
func main() {
fmt.Println("main函数被执行!")
}
func init() {
fmt.Println("init函数被执行!") // 先被执行
}
函数的执行顺序:
var num int = t() // 1、最先被执行,全部变量的定义
func t() int {
fmt.Println("test行数被执行")
return 10;
}
func main() {
fmt.Println("main函数被执行!") // 3、最后被执行
}
func init() {
fmt.Println("init函数被执行!") // 2、被执行
}
打印:
匿名函数
func main() {
// 定义匿名函数: 定义的同时调用
result := func(num1 int, num2 int) int {
return num1 + num2
}(10, 20)
fmt.Println(result) // 30
// 将匿名函数赋给一个变量,就是函数类型的变量
sub := func(num1 int, num2 int) int {
return num1 - num2
}
// 直接调用sub就等于调用匿名函数了
result2 := sub(30, 70)
fmt.Println(result2) // -40
}
闭包
// 求和操作
// 函数名是getSum,参数是空,
// getSum函数的返回值是一个函数,这个函数参数是int类型,返回值也是int类型
func getSum() func(int) int {
var sum int = 0 // 这个变量的值是保留状态,反复使用的感觉
return func(i int) int { // 返回的匿名函数 + sum(也就是匿名函数以外的变量),组成的一个整体
sum = sum + i
return sum
}
}
func main() {
f := getSum()
fmt.Println(f(1)) // 1
fmt.Println(f(2)) // 3
fmt.Println(f(3)) // 6
fmt.Println(f(4)) // 10
}
defer
func main() {
fmt.Println(add(30, 60))
}
func add(num1 int, num2 int) int {
// 遇到defer关键字,不会立马执行defer后面的代码,
// 会把defer后面的代码先压入栈,先执行下面的代码,defer后面的代码变量的值不会因为后面代码而改变
defer fmt.Println("num1=", num1)
defer fmt.Println("num2=", num2)
var sum int = num1 + num2
fmt.Println("sum=", sum)
return sum
}
打印:
字符串函数
func main() {
// 统计字符长度, len()
str := "golang你好" // 汉字是utf-8字符集,一个汉字是3个字节
fmt.Println(len(str)) // 12
// 对字符遍历:
// 方式1: for-range
for i, value := range str {
fmt.Printf("索引为:%d, 具体的值为:%c \n", i, value)
}
// 方式2: r:=[]rune(str)
r := []rune(str)
for i := 0; i < len(r); i++ {
fmt.Printf("%c \n", r[i])
}
// 字符串转整数
num1, _ := strconv.Atoi("666")
fmt.Println(num1)
// 整数转字符串
str1 := strconv.Itoa(88)
fmt.Println(str1)
// 统计一个字符串中有几个指定的字符
count := strings.Count("golanggolandjava", "go") // 包含了2个"go"
fmt.Println(count) // 2
// 不区分大小写比较字符串
flag := strings.EqualFold("hello", "HELLO")
fmt.Println(flag) // true
// 区分大小写比较字符串,使用==比较
fmt.Println("hello" == "HELLO") // false
// 返回指定字符在字符串出现的索引值,如果没有,返回-1
index := strings.Index("golanggolandjava", "and")
fmt.Println(index) // 9
// 字符串替换,最后一个参数表示替换几个,如果是2,那就替换2个,如果传-1表示全部替换
replaceStr := strings.Replace("gogogo", "go", "golang", 1)
fmt.Println(replaceStr) // golanggogo
// 根据某个字符分割标识,将字符串拆分成数组
splitStr := strings.Split("go-python-java", "-")
fmt.Println(splitStr) // [go python java]
// 字符串字母进行大小写转行
toLower := strings.ToLower("GO")
fmt.Println(toLower) // go
toUpper := strings.ToUpper("go")
fmt.Println(toUpper) // GO
// 去掉字符串两边空格
trimSpaceStr := strings.TrimSpace(" go and java ")
fmt.Println(trimSpaceStr) // go and java
// 去掉字符串左右两边指定的字符
trimStr := strings.Trim("~go~", "~")
fmt.Println(trimStr) // go
// 去掉字符串左边指定的字符
trimLeft := strings.TrimLeft("~go~", "~")
fmt.Println(trimLeft) // go~
// 去掉字符串右边指定的字符
trimRight := strings.TrimRight("~go~", "~")
fmt.Println(trimRight) // ~go
// 判断字符串是否以指定字符开头
hasPrefixStr := strings.HasPrefix("https://www.baidu.com/", "https")
fmt.Println(hasPrefixStr) // true
// 判断字符串是否以指定字符结尾
hasSuffixStr := strings.HasSuffix("https://www.baidu.com/", "https")
fmt.Println(hasSuffixStr) // false
}
日期时间函数
func main() {
// 时间和日期函数:
// 获取当前时间,调用time的Now()函数
now := time.Now()
// 返回一个结构体
fmt.Println(now) // 2022-06-16 09:05:29.8604469 +0800 CST m=+0.003519401
fmt.Printf("%v ~~~ 对应的类型是:%T \n", now, now) // 对应的类型是:time.Time
// 调用结构体中的方法:
fmt.Printf("年:%v \n", now.Year())
fmt.Printf("月:%v \n", now.Month())
fmt.Printf("日:%v \n", now.Day())
fmt.Printf("时:%v \n", now.Hour())
fmt.Printf("分:%v \n", now.Minute())
fmt.Printf("秒:%v \n", now.Second())
// 日期格式化
format := now.Format("2006/01/02 15/04/05") // 这个时间时go语言开发时的时间
fmt.Println(format) // 2022/06/16 09/17/41
// 任意格式
format2 := now.Format("01/02 15:04:05")
fmt.Println(format2) // 06/16 09:19:29
}
内置函数
常用内置函数:
func main() {
// len()函数,获取字符串长度
str := "golang"
fmt.Println(len(str)) // 6
// new()函数,分配内存,主要用来分配值的类型,基本数据类型
num := new(int)
// num的类型:*int, 值:0xc00000a0c0, 地址:0xc000006030, 指针指向的值:0
fmt.Printf("num的类型:%T, 值:%v, 地址:%v, 指针指向的值:%v", num, num, &num, *num)
// make()函数,分配内存,主要用来分配引用类型,指针、切片、map、管道等
}
《三体》中有句话——弱小和无知不是生存的障碍,傲慢才是。
所以我们不要做一个小青蛙