狂神说Go语言学习笔记(一)
狂神说Go语言学习笔记(二)
狂神说Go语言学习笔记(三)
一、什么是函数
func main() {
//调用函数 函数名()
fmt.Println(add(1, 2)) //3
}
func add(a, b int) int {
c := a + b
return c
}
二、函数的声明
func main() {
printinfo()
myprint("haha")
fmt.Println(add2(1, 2))
swap(1, 2)
}
// 无参返回值函数
func printinfo() {
fmt.Println("printinfo")
}
// 有一个参数的函数
func myprint(msg string) {
fmt.Println(msg)
}
// 有两个参数的函数
// 有一个返回值的函数
func add2(a, b int) int {
c := a + b
return c
}
// 有多个返回值的函数
func swap(a, b int) (int, int) {
return b, a
}
三、形式参数和实际参数
// max 两个数字比大小
// 形式参数:定义函数时,用来接收外部传入数据的参数,就是形式参数
// 实际参数:调用函数时,传给形参的实际数据叫做实际参数
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
//一个函数定义上有返回值,那么函数中必须使用return语句
//返回值
//调用处需要使用变量接受该结果
return result
}
四、可变参数
概念:一个函数的参数类型确定,但是个数不确定,就可以使用可变参数
func myfunc(arg ... int){}
//arg ... int 告诉go这个函数接收不定数量的参数,类型全部都是int
func main() {
getSum(1, 2, 3, 4, 5, 6, 7, 100)
}
func getSum(nums ...int) {
sum := 0
for i := 0; i < len(nums); i++ {
fmt.Println(nums[i])
sum += nums[i]
}
fmt.Println("sum:", sum)
}
五、值传递和引用传递
值传递
func main() {
//定义一个数组
arr1 := [4]int{1, 2, 3, 4}
fmt.Println("arr1默认数据", arr1)
update(arr1)
fmt.Print("arr1调用函数后数据", arr1)
//arr2的数据是从arr1复制来的,所以是不同的空间
//修改arr2并不会影响arr1
//值传递:传递的是数据的副本,修改数据,对于原始的数据没有影响
//值类型的数据,默认都是值传递,基础类型 array struct
}
func update(arr2 [4]int) {
fmt.Println("arr2接收数据", arr2)
arr2[0] = 10
fmt.Println("arr2修改后数据", arr2)
}
在内存中:
值传递类型的改变的都是原先数据的副本,两份分别在不同的内存空间中!,所以修改 arr1 , arr2 不会改变!
引用传递
// 引用传递
func main() {
//切片,可以扩容的数组
s1 := []int{1, 2, 3, 4}
fmt.Println("s1默认数据", s1)
update2(s1)
fmt.Print("s1调用函数后数据", s1)
}
func update2(s2 []int) {
fmt.Println("s2接收数据", s2)
s2[0] = 10
fmt.Println("s2修改后数据", s2)
}
在内存中:
s1 和 s2两个引用 都对同一片内存空间进行操作,改变s1 ,s2 当然也会变!
六、函数变量的作用域
//全局变量
var num int = 100
func main() {
//函数体内的局部变量
temp := 100
if b := 1 ; b<=10{
temp := 50
fmt.Println(temp)//局部变量,就近原则
fmt.Println(b)
}
fmt.Println(temp)
fmt.Println(num)
}
func f1() {
a := 1
fmt.Println(a)
fmt.Println(num)
}
func f2() {
b := 2
fmt.Println(b)
fmt.Println(num)
}
七、递归函数
func main() {
fmt.Println(getSum1(5)) //15
}
func getSum1(n int) int {
if n == 1 {
return 1
}
return getSum1(n-1) + n
}
八、defer
func main() {
f("1")
fmt.Println("2")
defer f("3")
fmt.Println("4") //1 2 4 3
}
func f(s string) {
fmt.Println(s)
}
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") //1 2 4 6 8 7 5 3
}
func f(s string) {
fmt.Println(s)
}
func main() {
a := 10
fmt.Println("a=", a)
//参数已经传递过去了 在最后执行
defer f(a)
a++
fmt.Println("end a=", a)
}
func f(s int) {
fmt.Println("函数里面的a=", s)
}
九、函数的本质
// func() 本身就是一个数据类型
func main() {
//f3 如果不加括号 函数就是个变量
//f3() 如果加了括号那就成了函数的调用
fmt.Printf("%T\n", f3) //func(int, int)
//定义函数类型的变量
var f5 func(int, int)
f5 = f3 //引用类型的
f5(1, 2)
}
func f3(a, b int) {
fmt.Println(a, b)
}
十、匿名函数
// 匿名函数
func main() {
h1()
h2 := h1 //函数本身也是一个变量
h2()
//匿名函数
h3 := func() {
fmt.Println("我是h3函数")
}
h3()
x1 := func(a, b int) int {
fmt.Println(a, b)
return a + b
//fmt.Println("我是h4函数")
}(1, 2)
fmt.Println(x1)
}
func h1() {
fmt.Println("我是h1函数")
}
十一、函数式编程
func main() {
r2 := oper(1, 2, add)
fmt.Println(r2) // 3
r3 := oper(1, 2, sub)
fmt.Println(r3) // -1
r4 := oper(8, 4, func(i int, i2 int) int {
if (i == 0) {
fmt.Println("除数不能为0")
return 0
}
return i / i2
})
fmt.Println(r4) // 2
}
//高阶函数,可以接受一个函数作为一个参数
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
}
func sub(a, b int) int {
return a - b
}
十二、闭包
/*
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回值就是这个内层函数
这个内层函数和外层函数的局部变量,统称为闭包结构
局部变量的生命周期就会发生改变 正常的局部变量会随着函数的调用而创建
随着函数的结束而销毁 但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁 因为内层函数还在使用
*/
func main() {
r1 := increment()
fmt.Println(r1) // 0x22e680
v1 := r1()
fmt.Println(v1) // 1
v2 := r1()
fmt.Println(v2) // 2
r2 := increment()
v3 := r2()
fmt.Println(v3) // 1
}
func increment() func() int {
//局部变量i
i := 0
//定义一个匿名函数,给变量自增并返回
fun := func() int {
i++
return i
}
return fun
}