函数

1.什么是函数

  • 函数是基本的代码块, 用于执行一个任务
  • Go语言最少有个main()函数
  • 可以通过函数来划分不同功能, 逻辑上每个函数执行的是指导的任务
  • 函数声明告诉了编译器的名称, 返回类型, 和参数

2.函数的声明

Go语言函数定义格式如下

func function_name( [parameter list])[return_types]{
    函数体
}
  • 无参无返回值函数
  • 有一个参数的函数
  • 有两个参数的函数
  • 有一个返回值的函数
  • 有多个返回值的函数
package main

func main() {

	//函数的调用
	printInfo()

	myPrint("哈哈")

	println(add2(1, 2))

	x, y := swap("aaa", "bbb")
	println(x, y)

}

//无参无返回值的函数
func printInfo() {
	println("printInfo")
}

func myPrint(msg string) {
	println(msg)
}

//有两个参数的函数, 有一个返回值的函数
func add2(a, b int) int {
	c := a + b
	return c
}

//有多个返回值的函数
func swap(x, y string) (string, string) {
	return y, x
}
package main

func main() {
	// 形参与实参要一一对应, 顺序, 个数, 类型
	max(1, 2)
}

// max 两个数字比大小
// 形式参数: 定义函数时, 用来接收外部传入数据的参数, 就是形式参数
// 实际参数: 调用函数时, 传给形参的实际数据叫做实际参数
func max(num1, num2 int) int {

	var result int
	if num1 > num2 {
		result = num1
	} else {
		result = num2
	}
	// 一个函数定义上有返回值, 那么函数中必须使用return语句
	//返回值
	//调用出需要使用变量接收该结果
	return result
}

3.可变参数

概念: 一个函数的参数类型确定, 但个数不确定, 就可以使用可变参数

func myFunc(arg ...int){}

//arg ...int 告诉go这个函数接收不定量的参数, 类型全部是int
package main

func main() {
	getSum(1, 2, 3, 4)
}

// ... 可变参数
func getSum(nums ...int) {

	sum := 0
	for i := 0; i < len(nums); i++ {
		sum += nums[i]
	}
	println(sum)
}

注意事项:

  • 如果一个函数的参数是可变参数, 同时还有其他的参数, 可变参数要放在列表的最后
  • 一个函数的参数列表中最多只能有一个可变参数

4.参数传递

按照数据的存储特点来分:

  • 值类型的数据: 操作的是数据本身, int、string、bool、float64、array...
  • 引用类型的数据: 操作的是数据的地址slice、map、chan...

值传递

package main

import "fmt"

func main() {
	//值传递
	//定义一个数组 [个数]类型
	arr := [4]int{1, 2, 3, 4}
	fmt.Println(arr)
	//传递: 拷贝arr
	update(arr)
	fmt.Println("调用更新方法后的数据: ", arr)
	//引用传递

}

func update(arr2 [4]int) {
	fmt.Println("arr2接收的数据: ", arr2)
	arr2[0] = 100
	fmt.Println("arr2修改后的数据: ", arr2)
}

引用传递

package main

import "fmt"

// 引用传递
func main() {
	// 切片, 可以扩容的数组
	s1 := []int{1, 2, 3, 4}
	fmt.Println("原来的数据", s1)
	update2(s1)
	fmt.Println("调用后的数据", s1)
}

func update2(s2 []int) {
	fmt.Println("传递的数据", s2)
	s2[0] = 100
	fmt.Println("修改后的数据", s2)
}

原来的数据 [1 2 3 4] 传递的数据 [1 2 3 4]
修改后的数据 [100 2 3 4] 调用后的数据 [100 2 3 4]

5.函数中变量的作用域

作用域: 变量可以使用的范围

局部变量: 函数内部定义的变量, 叫做局部变量

全局变量: 函数外部定义的变量, 叫做全局变量

package main

import "fmt"

//全局变量
var num int = 100

func main() {

	//函数体内的局部变量
	temp := 100

	if b := 1; b <= 10 {
		temp := 50
		fmt.Println(temp) //局部变量就近原则
		fmt.Println(b)
	}
	fmt.Println(temp)
}

func f1() {
	a := 1

	fmt.Println(a)
	fmt.Println(num)
}

func f2() {
	//fmt.Println(a)
	fmt.Println(num)
}

6.递归函数

定义: 一个函数自己调用自己, 就叫做递归函数

注意: 递归函数需要有一个出口, 逐渐向出口靠近, 没有出口就会形成死循环

求和: 1...5

package main

import "fmt"

func main() {
	sum := getSum(5)

	fmt.Println(sum)
}

func getSum(n int) int {
	if n == 1 {
		return 1
	}
	return getSum(n-1) + n
}

7.defer

defer语义: 推迟、延迟

在go语言中, 使用defer关键字来延迟一个函数或者方法的执行.

defer函数或者方法: 一个函数或方法的执行被延迟了

  • 你可以在函数中添加多个defer语句, 当函数执行到最后时, 这些语句会按照逆序执行, 最后该函数返回,特别是当你在进行一些打开资源操作时, 遇到错误需要提前返回, 在返回前你需要关闭相应的资源, 不然很容易造成资源泄露等问题
  • 如果你有很多调用defer, 那么defer是采用 后进先出(栈)模式
package main

import "fmt"

func main() {
	f("1")
	fmt.Println(" 2")
	defer f("3") //会被延迟到最后执行
	fmt.Println("4")
	defer f("5") //会被延迟到最后执行
	fmt.Println("6")
	defer f("7") //会被延迟到最后执行
	fmt.Println("8")
	defer f("9") //会被延迟到最后执行
	fmt.Println("10")
}

func f(s string) {
	fmt.Println(s)
}
package main

import "fmt"

func main() {
	a := 10
	fmt.Println("a=", a)
	defer f(a)
	a++
	fmt.Println("end=", a)
}

func f(s int) {
	fmt.Println("函数里面的a=", s)
}

8.高级: 函数的数据类型

package main

import "fmt"

//func本身就是一个数据类型
func main() {
	// f1() 如果加了括号就成了函数的调用
	fmt.Printf("%T\n", f1)      // func()
	fmt.Printf("%T\n", 10)      // int
	fmt.Printf("%T\n", "hello") // string
}

func f1() {

}

9.高级: 匿名函数

package main

import "fmt"

// 匿名函数
func main() {

	f1()
	f2 := f1 // 函数本身也是一个变量
	f2()

	// 匿名函数
	f3 := func() {
		fmt.Println("我是f3函数")
	}
	f3()

	//f4
	func() {
		fmt.Println("我是f4函数")
	}()

	func(a, b int) {
		fmt.Println(a, b)
		fmt.Println("我是f5函数")
	}(1, 2)

	r1 := func(a, b int) int {
		fmt.Println("我是f6函数")
		return a + b
	}(1, 2)
	fmt.Println(r1)
}

func f1() {
	fmt.Println("我是f1函数")
}

10.高级: 回调函数

高阶函数: 根据go语言的数据类型的特点, 可以将一个函数作为另一个函数的参数.

fun1(), fun2()

将fun1函数作为fun2这个函数的参数

fun2函数: 就叫做高阶函数, 接收了一个函数作为参数的函数

fun1函数: 就叫做回调函数, 作为另外一个函数的参数

package main

import "fmt"

func main() {
	r1 := add(1, 2)
	fmt.Println(r1)
	r2 := oper(3, 4, add)
	fmt.Println(r2)
}

//高阶函数
func oper(a, b int, fun func(int, int) int) int {
	r := fun(a, b)
	return r
}

func add(a, b int) int {
	return a + b
}

11.高级: 闭包

一个外层函数中, 有内层函数, 该内层函数中, 会操作外层函数的局部变量,并且该外层函数的返回值就是这个内层函数.这个内层函数和外层函数的局部变量, 统称为闭包结构

局部变量的生命周期就会发生改变, 正常的局部变量会随着函数的调用而创建, 随着函数的结束而销毁,但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁, 因为内层函数还在继续使用

package main

import "fmt"

func main() {
	r1 := increment()
	fmt.Println(r1)

	v1 := r1()
	fmt.Println(v1)

	v2 := r1()
	fmt.Println(v2)
	fmt.Println(r1())
	fmt.Println(r1())
	fmt.Println(r1())

	//
	r2 := increment()
	v3 := r2()
	fmt.Println(v3)
	fmt.Println(r1())
	fmt.Println(r2())
}

//自增
func increment() func() int {
	//局部变量i
	i := 0
	// 定义一个匿名函数, 给变量自增并返回
	fun := func() int { //内层函数没有执行
		i++
		return i
	}
	return fun
}