【Golang】四、基础篇 -- 函数

本章主要介绍golang函数的一些使用,包含了一些自定义函数和系统函数的使用和设计原则,希望可以帮助到各位同学理解并且使用。

一、包的引入

在环境变量中配置$GOPATH的环境变量,在src路径下建立文件,可以识别到该包。

二、递归

写一个递归我们需要知道,这个函数的出口在哪?这个函数的规律是什么?才可以写出递归函数

  1. 斐波那数列
    (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

    1. f(5) = f(4) + f(3)
    2. f(4) = f(3) + f(2)
    3. f(3) = f(2) + f(1)
    4. f(2) = 1,f(1) = 1
      由此可知:
    5. f(3) = f(2) + f(1) = 1 + 1 = 2
    6. 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)		
    
  2. 经典算法问题:猴子吃桃

    **描述:**猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个; 第二天,吃剩下的桃子的一半,还不过瘾,又多吃了一个;以后每天都吃前一天剩下的一半多一个,到第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)案例执行顺序:

  1. main.go
    import (
    	"fmt"
    	other "go_code/package04/other"
    )
    func init() {
    	fmt.Println("2. init")
    }
    func main() {
    	fmt.Println("3. main")
    }
    
  2. other.go
    package other
    import "fmt"
    func init() {
    	fmt.Println("1. fnc init")
    }
    
  3. 输出
    1. fnc init
    2. init
    3. main

七、匿名函数

  1. 内部匿名函数
    res := func (i int, k int) int {
    	return i + k
    }
    // 调用
    fmt.Println(res(1, 2))
    
  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

十、系统函数:字符串

  1. 字符串遍历

    // 转化为rune切片
    r := []rune(str)
    fmt.Printf("%c\n", r)
    
  2. string => int
    atoi, err := strconv.Atoi(str)

  3. int => string
    itoa := strconv.Itoa(111)

  4. Contains 包含
    strings.Contains("jjjj.jpg", "j.jp")

  5. Count 子串出现次数
    strings.Count("aaaaaaccc", "cc")

  6. EqualFold 比较字符串
    strings.EqualFold("AAA", "aaa") 不区分大小写

  7. Index 字串第一次出现的位置
    strings.Index("sdfaaa","s")

  8. LastIndex 字串最后一次出现的位置
    strings.LastIndex("sdfaaa","a")

  9. Replace 替换指定字符串

    参数详解Replace(需要替换的字符串, 需要替换的字段, 新字段, 替换几个(-1默认全部替换))

    strings.Replace("aaabaaabaaabaaa", "aaa", "!!!", -1)

  10. Split 分割字符串
    strings.Split("aa,aa,aa", ",")

  11. 大小写转换

    fmt.Println(strings.ToLower("GO")) // 转小写
    fmt.Println(strings.ToUpper("go_to_the_zeo")) // 转大写
    
  12. TrimSpace 去除前后空格
    strings.TrimSpace(" aaa ")

  13. Trim 去除指前后定子字符串
    strings.Trim("!!aababaa","!a")

  14. 是否包含指定前缀活着后缀

    // 前缀
    strings.HasPrefix("http://www.baidu.com", "http://") 
    //后缀
    strings.HasSuffix("test.png", ".png") 
    
  15. Fields 按照一或者多个连续的空格分割字符串
    空字符串则返回空切片

    strings.Fields("s.   s.   s.  a") => ["s.","s.","s.","a"]
    

十一、系统函数:日期

  1. Now 获取当前日期
    time.Now()
  2. 获取年月日…
    time.Now().Year()
    int(time.Now().Month()) //使用int转化月份
    time.Now().Day()
    // .......
    
  3. Format 格式化日期
    time.Now().Format("2006-01-02 15:04:05")
  4. Parse 格式化日期字符串
    time, _ := time.Parse("2006/01/02 15:04:05", "2021/01/31 18:00:00")
    
  5. Sleep 睡眠
    time.Sleep(2 * time.Second())
  6. 随机数种子
    rand.Seed(time.Now().UnixNano())
    rand.Intn(10)
    

十二、错误处理

(1)捕获异常

golang中捕获异常使用,deferrecover捕获异常

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)自定义异常

  1. 定义一个异常
    var err = errors.New("是谁?")
  2. 抛出异常
    panic(err)
  3. 案例来一个:
// 判断传入的值是否是"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)
	}
}

附:函数细节

  1. 行参列表可以传递多个值,返回列别也可以是多个值
  2. 行参列表和返回值列表可以返回值类型(具体的数据类型)、引用类型(如指针)
  3. 函数名大写表示是public公共函数,小写默认private私有
  4. 不支持函数重复载
  5. 一个包下不允许有相同函数名
  6. _表示忽略返回值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼小鱼啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值