golang的函数
本章主要介绍golang函数的一些使用,包含了一些自定义函数和系统函数的使用和设计原则,希望可以帮助到各位同学理解并且使用。
一、包的引入
在环境变量中配置$GOPATH
的环境变量,在src路径下建立文件,可以识别到该包。
二、递归
写一个递归我们需要知道,这个函数的出口在哪?这个函数的规律是什么?才可以写出递归函数
-
斐波那数列
(1)数列格式:1 1 2 3 5 8 13 。。。。
(2)规律:f(n) = f(n-1) + f(n+1)
(3)出口:n:1,n:2
(4)函数:func f(n int) int { if n == 1 || n == 2 { return 1 } else { return f(n-1) + f(n-2) } }
(5)分析:
假设:n = 5
- f(5) = f(4) + f(3)
- f(4) = f(3) + f(2)
- f(3) = f(2) + f(1)
- f(2) = 1,f(1) = 1
由此可知: - f(3) = f(2) + f(1) = 1 + 1 = 2
- f(4) = f(3) + f(2) = f(2) + f(1) + f(2) = 1 + 1 + 1 = 3
…
依次类推一层一层递归就可以求出
f(5)
的值!
递归到第一位和第二位就会停止函数递归,此时返回1函数一层一层从函数栈区中往上回收函数(6)优化:
由于上述代码的复杂度太高,会导致内存溢出,可以使用尾递归优化来实现更快速的斐波那契数列func fb(a, b, n) { if n > 2 { return fb(a + b, a, n-1) } else { return a } } fb(1,1,10)
-
经典算法问题:猴子吃桃
**描述:**猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个; 第二天,吃剩下的桃子的一半,还不过瘾,又多吃了一个;以后每天都吃前一天剩下的一半多一个,到第10天想再吃时,只剩下一个桃子了。问第一天共吃了多少个桃子
解题思路: 以知第10天剩余1个桃,所以出口就是10天1个桃。题意说明每一天都前一天剩下的一半多的挑子再多吃一个,所以第9天吃的桃子就是(1 + 1)* 2 = 4个,由此可以推断:n表示第多少天,第n天吃的桃子就是,2 *(1 + houzi(n + 1))个桃子
代码:
func houzi(n int) int { if n == 10 { return 1 } else { return 2 * (houzi(n + 1) + 1) } } //9: (houzi(1) + 1) * 2 //-> f(5) -> 2 * houzi(4) + 1 = 31 //-> f(4) -> 2 * houzi(3) + 1 = 15 //-> f(3) -> 2 * houzi(2) + 1 = 7 //-> f(2) -> 2 * houzi(1) + 1 = 2 * (1 + 1) = 4
三、函数变量
函数也是一种数据类型,可以赋值给一个变量,则该变量就是函数变量了。通过该变量可以对函数进行调用!!!
func main() {
// 将 add变量 = addFunc函数
add := addFunc
// 可以直接调用函数
add(1, 3)
// 参数1: 函数
// 参数2、3:具体参数
myFunc(add, 1, 2)
}
// 调用函数
func myFunc(addFun func(int, int) int, a int, b int) {
fmt.Println(addFun(a, b))
}
// 作为参数的函数
func addFunc(a int, b int) int {
return a + b
}
四、自定义数据类型
支持自定义数据类型,type相当于起了一个别名
// myInt等价于int8
type myInt int8
// myFunc等价于func(int, int) int
type myFunc func(int, int) int
案例:
type myFunc func(int, int) int func myAddFunc(addFun myFunc, a int, b int) { fmt.Println(addFun(a, b)) }
两个
myAddFunc
函数定义的第一个行参相互等价func myAddFunc(addFun func(int, int) int, a int, b int) { fmt.Println(addFun(a, b)) }
五、自定义返回值名称
func main() {
sum, avg := getSum(1,3)
fmt.Println(sum, avg)
}
func getSum(n1 int, n2 int)(sum int,avg int){
sum = n1 + n2
avg = (n1 + n2)/2
// 这里可不用写返回值,返回会自动封装sum和avg
return
}
五、可变参数
args...
需要放在行参列表最后
import (
"fmt"
other "go_code/package04/other"
)
func main() {
ArgsFunc("测试:",1,2,3,4,9,6)
}
func ArgsFunc(param string, args... int) {
fmt.Println(param)
// 遍历args参数
for index, item := range args {
fmt.println(index, ":", item)
}
}
// 输出 =>
// 测试:
// 0 : 1
// 1 : 2
// 2 : 3
// 3 : 4
// 4 : 9
// 5 : 6
六、init函数
每一个源文件都有一个init函数
,会在 main函数
之前调用,文件最先加载import导入的文件,所以先import先调用init函数
(1)执行顺序
文件中包含全局变量,init函数,main函数,执行顺序为:全局变量定义 -> init() -> main()
(2)案例执行顺序:
- main.go
import ( "fmt" other "go_code/package04/other" ) func init() { fmt.Println("2. init") } func main() { fmt.Println("3. main") }
- other.go
package other import "fmt" func init() { fmt.Println("1. fnc init") }
- 输出
- fnc init
- init
- main
七、匿名函数
- 内部匿名函数
res := func (i int, k int) int { return i + k } // 调用 fmt.Println(res(1, 2))
- 全局匿名函数
var Res = func (i int, k int) int { return i + k } func mian() { // 调用 fmt.Println(Res(1,2)) }
八、闭包
可以理解为,方法返回一个方法!进行调用!(返回方法存放在makeJoin栈中)
闭包可以把返回的匿名函数一直存放在方法栈中,不销毁!多次引用无需对外层函数和变量重新赋值
func makeJoin(suffix string) func(string) string {
// 方法初始化后,这个变量在第二次调用时不会重新初始化。
var i int = 1
return func(fileName string) string{
//
i++
fmt.Println(i)
if strings.HasSuffix(fileName, suffix) {
return fileName
} else {
return fileName + suffix
}
}
}
func main() {
// 调用闭包,可以理解为 make接收一个方法的方法
make := makeJoin(".bat")
// 这里就是针对makeJoin返回的方法进行调用
fmt.printLn(make("aaaa1.bat"))
fmt.printLn(make("aaaa2"))
// 输出
// aaaa1.bat
// aaaa2.bat
}
说明: strings.HasSuffix(字符串, 后缀)
可以用来匹配字符串
九、defer 延时机制
说明:defer是在函数执行完成时调用(defer相当于入栈,先进后出)
func deferTest() {
defer fmt.Println("print:deffer1")
defer fmt.Println("print:deffer2")
fmt.Println("print:",1)
}
func main() {
deferTest()
}
// 输出:
// print:1
// print:deffer2
// print:deffer1
十、系统函数:字符串
-
字符串遍历
// 转化为rune切片 r := []rune(str) fmt.Printf("%c\n", r)
-
string => int
atoi, err := strconv.Atoi(str)
-
int => string
itoa := strconv.Itoa(111)
-
Contains 包含
strings.Contains("jjjj.jpg", "j.jp")
-
Count 子串出现次数
strings.Count("aaaaaaccc", "cc")
-
EqualFold 比较字符串
strings.EqualFold("AAA", "aaa")
不区分大小写 -
Index 字串第一次出现的位置
strings.Index("sdfaaa","s")
-
LastIndex 字串最后一次出现的位置
strings.LastIndex("sdfaaa","a")
-
Replace 替换指定字符串
参数详解Replace(需要替换的字符串, 需要替换的字段, 新字段, 替换几个(-1默认全部替换))
strings.Replace("aaabaaabaaabaaa", "aaa", "!!!", -1)
-
Split 分割字符串
strings.Split("aa,aa,aa", ",")
-
大小写转换
fmt.Println(strings.ToLower("GO")) // 转小写 fmt.Println(strings.ToUpper("go_to_the_zeo")) // 转大写
-
TrimSpace 去除前后空格
strings.TrimSpace(" aaa ")
-
Trim 去除指前后定子字符串
strings.Trim("!!aababaa","!a")
-
是否包含指定前缀活着后缀
// 前缀 strings.HasPrefix("http://www.baidu.com", "http://") //后缀 strings.HasSuffix("test.png", ".png")
-
Fields 按照一或者多个连续的空格分割字符串
空字符串则返回空切片strings.Fields("s. s. s. a") => ["s.","s.","s.","a"]
十一、系统函数:日期
- Now 获取当前日期
time.Now()
- 获取年月日…
time.Now().Year() int(time.Now().Month()) //使用int转化月份 time.Now().Day() // .......
- Format 格式化日期
time.Now().Format("2006-01-02 15:04:05")
- Parse 格式化日期字符串
time, _ := time.Parse("2006/01/02 15:04:05", "2021/01/31 18:00:00")
- Sleep 睡眠
time.Sleep(2 * time.Second())
- 随机数种子
rand.Seed(time.Now().UnixNano()) rand.Intn(10)
十二、错误处理
(1)捕获异常
golang中捕获异常使用,defer
、recover
捕获异常
recover
是内置函数,可以捕获运行过程中抛出得异常
func paochu() {
// 2、捕获异常
defer func(){
err := recover() //
if err != nil {
fmt.Println("errors:", err)
}
}()
num1 := 10
num2 := 0
// 1、 抛出 异常
i := num1/num2
fmt.Println(i)
}
(2)自定义异常
- 定义一个异常
var err = errors.New("是谁?")
- 抛出异常
panic(err)
- 案例来一个:
// 判断传入的值是否是"A.new"
func readFunc(str string) (error error, suc int) {
if str == "A.new" {
suc = 1
error = nil
} else {
suc = 0
error = errors.New("错误了")
}
return
}
func main(){
// 接收err和sec
err, sec := readFunc("A.new")
if err != nil {
// 抛出异常
panic(err)
} else {
fmt.Println(sec)
}
}
附:函数细节
- 行参列表可以传递多个值,返回列别也可以是多个值
- 行参列表和返回值列表可以返回值类型(具体的数据类型)、引用类型(如指针)
- 函数名大写表示是public公共函数,小写默认private私有
- 不支持函数重复载
- 一个包下不允许有相同函数名
_
表示忽略返回值