Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

01 上节回顾

数据类型:

整型和浮点型 布尔型 字符串

默认值:

nil 空对象

数字 var arr [3]int

默认值

package main

import "fmt"

func main() {
   //零值
   var s string
   var x int
   var arr [3]int //数组是值类型
   var b bool
   fmt.Println(s)
   fmt.Println(x)
   fmt.Println(arr) //[0 0 0 ]
   fmt.Println(b)   //false
}

数组回顾:

[3]int 和[4]int是俩类型

声明并赋值 var arr=[3]int{1,2,3}

不限长数组 [...]int{1,2,3}

 

查和改没问题 a:=arr[1] arr[1]=5

访问:直接查询,遍历循环

var arr=[3]int{1,2,3}
for i,v:=range arr {
   fmt.Println(i,v)
} 

切片回顾:

起始位置地址,长度,容量

1、定义s1,从数组上切

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

 

2 再扩容s1,100换成500

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

 

3 对s4扩容

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

4 重新赋给s1,s1的地址没变,只是把3改为4

 Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

  案例 切片本质

package main

import (
   "fmt"
)

func main() {
/* //切片本质1
   var s = [5]int{1,2,3,4,5}
   s1:=s[0:3]
   s2:=s[2:4]
   fmt.Println("s1:",s1,len(s1),cap(s1))
   fmt.Println("s2:",s2,len(s2),cap(s2))
   s3:=append(s1,100)
   fmt.Println(s3,len(s3),cap(s3))
   s4:=append(s3,500)
   fmt.Println(s4,len(s4),cap(s4))
   s5:=append(s4,1000)
   fmt.Println(s5,len(s5),cap(s5))*/

   //切片本质2
   var s = [5]int{1,2,3,4,5}
   s1:=s[0:3]
   fmt.Printf("%p\n",s1) //0xc00000c330
   s1=append(s1,100)
   fmt.Printf("%p\n",s1) //0xc00000c330
}

02 回顾 map

hash结构

数组根据索引查找

map根据键查找

map声明 赋值

package main

import "fmt"

func main() {
   //切片
   var s1 = make([]int,3)
   fmt.Println(s1) // [0 0 0]
   //map1
   var m1 map[string]string
   m1=make(map[string]string)
   m1["name"]="yuan"
   m1["age"]="22"
   fmt.Println(m1) //map[age:22 name:yuan]
}

interface{}类型

定义map的值为interface{}类型

 

package main

import "fmt"

func main() {
   //示例1 map interface{}类型
   m2:=make(map[string]interface{})
   m2["name"]="yuan"
   m2["age"]=22
   m2["ISMarried"]=false
   fmt.Println(m2) //map[ISMarried:false age:22 name:yuan]

   // 示例2 map interface{}类型
   m3:=map[string]interface{}{"name":"yuan","age":22}
   fmt.Println(m3) //map[age:22 name:yuan]
}

map查询

取1002学生第二科目成绩

package main

import "fmt"

func main() {
   //取1002学生第2科目成绩
   var m4 = map[string][]int{"1001":{100,90,80},"1002":{89,90,91}}
   fmt.Println(m4["1002"][1]) //90

}

map遍历

package main

import "fmt"

func main() {

    //取1002学生第2科目成绩
   var m4 = map[string][]int{"1001":{100,90,80},"1002":{89,90,91}}
   fmt.Println(m4["1002"][1]) //90

   for k,v:=range m4 {
      fmt.Println(k,v)
   }
   fmt.Println("m4长度:",len(m4)) //m4长度: 2
}

遍历map对应的切片元素

package main

import "fmt"

func main() {

//取
   var m4 = map[string][]int{"1001":{100,90,80},"1002":{89,90,91}}

   for k,v:=range m4 {
      fmt.Println(k,v)
      for i,v2:=range v {
         fmt.Printf("i:%d v2:%d\n",i,v2)
      }
   }
}

打印结果

1001 [100 90 80]

i:0 v2:100

i:1 v2:90

i:2 v2:80

1002 [89 90 91]

i:0 v2:89

i:1 v2:90

i:2 v2:91

03 回顾 函数

函数声明和调用

什么是函数? 代码的组织形式

函数声明和调用

参数

参数是个赋值的过程,值拷贝

参数什么意思?为了能够在函数调用的时候动态传入一些值给函数

func xunhuan(m map[string][]int) {
   for k,v:=range m {
      fmt.Println(k,v)
      for i,v2:=range v {
         fmt.Printf("i:%d,v2:%d",i,v2)
      }
   }
}

位置参数

必备参数,传入实际参数,参数位置和数量必须与定义的一致

可变参数

可变参数(不定长参数)通常放在函数最后

package main

import "fmt"

func sum(base int,nums ...int)int {
   fmt.Println(base,nums)
   sum:=base
   for _,v:=range nums {
      sum+=v
   }
   return sum
}

func main() {
   ret:=sum(10,2,3,4)
   fmt.Println(ret)
}
打印结果
10 [2 3 4]
19

返回值

没有返回值,不能赋值给一个新的变量

案例 返回值

package main

import "fmt"

func xunhuan(m map[string][]int) int {
   var ret = 0
   for _,scores :=range m {
      //fmt.Println(sid,scores)
      var s=0
      for _,v2:=range scores {
         s+=v2
      }
      average :=s/3
      //fmt.Println(sid,"的average",average)
      ret+=average
   }
   mAverage:=ret/len(m)
   //fmt.Println("mAverage",mAverage)
   return mAverage
}

func main() {

   var m3=map[string][]int{"1001":{100,90,80},"1002":{91,90,89}}
   fmt.Println("1002:::",m3["1002"][1])
   var m4=map[string][]int{"1003":{100,90,80},"1004":{91,90,89}}
   //函数
   var r1 = xunhuan(m3)
   fmt.Println("r1",r1)
   var r2 = xunhuan(m4) //m4是实际参数
   fmt.Println("r2",r2)
}

返回值命名

func calc(x,y int) (sum,sub int) {
   sum = x+y
   sub = x-y
   return //return sum sub
}

func main() {
   //返回值
   var a,c = calc(2,1)
   fmt.Println(a,c)
}

重新赋值

func calc(x,y int) (sum,sub int) {
   sum = x+y
   sub = x-y
   //return //return sum sub
   return 5,6
}

04 指针类型

package main

import (
   "fmt"
   "reflect"
)

func main() {
   //声明赋值一个整型变量
   var x int
   x = 100    //存整型数字的变量称为整型变量
   var p *int //存地址的变量称为指针变量 *类型
   p = &x
   fmt.Println(p, reflect.TypeOf(p))
}

案例

package main

import (
   "fmt"
   "reflect"
)

func main() {
/* var x=10
   fmt.Println(&x) //0xc00000a0b8
   x=100
   fmt.Println(&x) //0xc00000a0b8

   var s = "hello"
   fmt.Println(s)*/

   //声明赋值一个整型变量
   //var x int = 100
   var x int
   x = 100
/* var p=&x
   fmt.Println(p,reflect.TypeOf(p)) //0xc00000a0b8 *int*/
   var p *int
   // fmt.Println(p,*p) //思考*p等于什么? panic: runtime error: invalid memory address or nil pointer dereference
   p=&x
   fmt.Println(p,reflect.TypeOf(p)) //0xc00000a0b8 *int
}

05 值传递

案例1

x和y地址一样不一样?

package main

import "fmt"

func main() {
   //案例1
   var x=10
   fmt.Printf("x的地址%p\n",&x) //x的地址0xc00000a0b8
   y:=x
   fmt.Printf("y的地址%p\n",&y) //y的地址0xc00000a0f0
   x=100
   fmt.Println(y) //10
}

案例2 切片拷贝

package main

import "fmt"

func main() {
   //案例2
   var a=[]int{1,2,3}
   b:=a
   a[0]=100
   fmt.Println(b) //[100 2 3]
}

案例1 函数传参-传值

package main

import "fmt"

func func01(a int) {
   //fmt.Println(x)
   a=100
}

func main() {
   //函数传参
   //案例1
   var x=10
   func01(x) //本质是a=x 把10赋给a
   fmt.Println(x) //10
}

案例2 函数传参-传地址

希望打印修改后的值

一切为值拷贝

把p的值(x的地址),拷贝一份

package main

import "fmt"

func  func02(a *int)  {
   fmt.Println(a) //x的地址
   fmt.Println(*a)
}

func main() {
   //函数传参
   //案例2
   var x=10
   var p *int=&x
   fmt.Println(p)
   func02(p) //a=p

   fmt.Println(":::",x)
}

打印结果

0xc00000a0b8

0xc00000a0b8

10

::: 10

本质:通过切片和传地址实现引用的修改效果

b=*a 是把地址对应的值取出来,b=*a 赋给了变量

练习 函数传参

package main

import "fmt"

func func02(s []int) {
   fmt.Printf("func02的s的地址:%p\n",&s)
   s[0]=100
}

func func03(p *int) {
   *p=100
}

func main() {
   //案例2
   var s=[]int{1,2,3}
   fmt.Printf("main的s的地址:%p\n",&s) //main和func02中s地址不一样√
   func02(s)
   fmt.Println(s)

   //案例3
   var a=10
   var p *int =&a
   func03(p)
   fmt.Println(a)
}

06 下午 函数传参的案例

传参的过程,就是拷贝的过程,再传参

不同点在于: 指针传的是地址,切片指的是底层数组

案例2传地址回顾

取址和赋值

package main

import (
   "fmt"
   "reflect"
)

func  func02(a *int)  {
   fmt.Println(a) //x的地址
   fmt.Println(*a,reflect.TypeOf(*a)) //10 int
*a = 100 } func main() { //案例 var x=10 var p *int=&x fmt.Println(p) func02(p) //a=p fmt.Println(":::",x) }

 

案例3传切片

package main

import (
   "fmt"
   "reflect"
)
func func03(s []int) {
   fmt.Printf("func02的s的地址:%p\n",&s)
   s[0] = 100
   // s = append(s, 1000)
}
func main() {
//案例3
   var s = []int{1, 2, 3}
   fmt.Printf("main的s的地址:%p\n",&s)
   func03(s)
   fmt.Println(s)
}

打印结果
main的s的地址:0xc000096060
func02的s的地址:0xc000096078
[100 2 3]

  

案例3图解

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

Day04 Go语言函数:值传递、匿名函数、高阶函数、闭包和defer语句

 

 

 

 

声明调用

参数

返回值

作用域

传参

 

07下午 匿名函数

 

函数里边只能生成匿名函数

 

匿名函数的使用

方式1

package main

import "fmt"

func main() {
   //匿名函数的使用
   var foo= func () {
      fmt.Println("hello yuan")
   }
   foo() //hello yuan
}

方式2:只用1次,就不用命名了

本质:是把函数整体赋值给bar变量了

package main

import "fmt"

func main() {
   //方式2
   func() {
      fmt.Println("hello yuan")
   }() //hello yuan

   //匿名函数传参
   (func(x,y int){
      fmt.Println(x+y)
   })(4,5) //9
}

  

匿名函数的形参和实参

package main

import "fmt"

func main() {
   //匿名函数
   var foo= func () {
      fmt.Println("hello yuan")
   }
   foo() //hello yuan

   //方式2
   func() {
      fmt.Println("hello yuan")
   }() //hello yuan

   (func(x,y int){
      fmt.Println(x+y)
   })(4,5) //9
}

练习

package main

import "fmt"

func main() {
   //匿名函数
   var foo= func () {
      fmt.Println("hello yuan")
   }
   foo()

   //方式2
   func() {
      fmt.Println("hello yuan")
   }()
   
   (func(x,y int){
      fmt.Println(x+y)
   })(4,5)
}

08高阶函数

package main

import "fmt"

func bar() {
   fmt.Println("bar")
}

func main() {
   var foo = bar
   foo()
}

设计一个函数,打印出来执行的时间

时间操作

package main

import (
   "fmt"
   "time"
)

func main() {
   //引入时间操作
   fmt.Println(time.Now().Unix())
   time.Sleep(time.Second*2)
   fmt.Println(time.Now().Unix())
}

函数类型

函数类型有区别,函数的类型会根据参数和返回值变化

package main

import (
   "fmt"
   "reflect"
)

func bar1() {
   fmt.Println("hello")
}
func bar2(x,y int) {
   fmt.Println(x+y)
}
func bar3(x,y int) int {
   return x+y
}

func main() {
   fmt.Println(bar1,reflect.TypeOf(bar1)) //0xc3c560 func()
   fmt.Println(bar2,reflect.TypeOf(bar2)) //0x3bc5e0 func(int, int)
   fmt.Println(bar3,reflect.TypeOf(bar3)) //0xbdc660 func(int, int) int
}

以函数作为参数

课堂练习

package main

import (
   "fmt"
   "time"
)

func bar() {
   time.Sleep(time.Second*3)
   fmt.Println("bar")
}

func foo() {
   time.Sleep(time.Second*2)
   fmt.Println("foo")
}

func funcTimer(f func()) {
   t1:=time.Now().Unix()
   f()
   t2:=time.Now().Unix()
   fmt.Println("spend time:",t2-t1)
}

func main() {


/* //引入时间操作
   fmt.Println(time.Now().Unix())
   time.Sleep(time.Second*2)
   fmt.Println(time.Now().Unix())

   fmt.Println(foo,reflect.TypeOf(foo))*/

   funcTimer(foo)
   funcTimer(bar)
}

09 双值计算器-函数作为参数

package main

import "fmt"

//双值运算器
func add(x,y int)int {
   return x+y
}

func mul(x,y int)int {
   return x*y
}

func cal(a,b int,calFunc func(int,int) int) {
   ret:=calFunc(a,b)
   fmt.Println(ret)
}

func main() {
   cal(10,5,mul)
}

10高阶函数2

以函数作为返回值

package main

import (
   "fmt"
)

func foo() func(int,int) string{
   /*var bar = func(x,y int) string {
      fmt.Println("bar...")
      return "bar"
   }
   return bar*/
   return  func(x,y int)string{
      fmt.Println("bar...")
      return "bar"
   }
}

func main() {
   var ret = foo()
   ret(1,2)
}

函数作为返回值,里边俩函数问题?看return的函数,a会飘红

import "fmt"

func foo() func(int,int) string {
   var bar = func(x,y int) string {
      fmt.Println("bar...")
      return "bar"
   }
   var a=func(x,y int) string {
      return "a"
   }
   return bar
}

11闭包

闭包是引用了*变量的函数。 *变量:外部非全局变量

闭包是: 函数+所依赖的环境

package main

import (
   "fmt"
)

func foo() func() {
   var x=100
   var bar = func() {
      fmt.Println("bar",x)
   }
   return bar
}

func main() {
   var f = foo() //闭包函数
   f()
}

ret只接收了返回值,拷贝了return的结果

package main

import (
   "fmt"
)

func a() int {
   i:=100
   fmt.Println(i)
   return 100
}

func main() {
   var ret = a()
   fmt.Println(ret)
}

闭包会对内存造成一定的压力

12 闭包应用-装饰函数

记录被调用次数的功能

package main

import "fmt"

//装饰函数: 记录某个函数的调用次数

var callerCount = 0

func counter(f func()) {
   f()
   callerCount++
}

func foo() {
   fmt.Println("foo function调用")
}

func main() {
   counter(foo)
   counter(foo)
   counter(foo)
   counter(foo)
   counter(foo)
   fmt.Println(callerCount)
}

修改

package main

import "fmt"

//装饰函数: 记录某个函数的调用次数
func counter(f func()) func() {
   var callerCount = 0
   return func() {
      f()
      callerCount++
      fmt.Println("调用次数:",callerCount)
   }
}

//测试的调用函数
func foo() {
   fmt.Println("foo function调用")
}

func main() {
   newFoo:=counter(foo)
   newFoo()
   newFoo()
}

调用bar函数

package main

import "fmt"

//装饰函数: 记录某个函数的调用次数
func counter(f func()) func() {
   var callerCount = 0
   return func() {
      f()
      callerCount++
      fmt.Println("调用次数:",callerCount)
   }
}

//测试的调用函数
func foo() {
   fmt.Println("foo function调用")
}

func bar() {
   fmt.Println("bar function调用")
}

func main() {
   newFoo:=counter(foo)
   newBar:=counter(bar)
   newFoo()
   newFoo()
   newBar()
}

开放封闭性

package main

import "fmt"

//装饰函数: 记录某个函数的调用次数
func counter(f func()) func() {
   var callerCount = 0
   return func() {
      f()
      callerCount++
      fmt.Println("调用次数:",callerCount)
   }
}

//测试的调用函数
func foo() {
   fmt.Println("foo function调用")
}

func main() {
   foo:=counter(foo)
   foo()
}

闭包练习题

package main

import "fmt"

func foo() {
   fmt.Println("foo function调用")
}

func main() {
   fmt.Println("test01")
   defer foo() //延迟调用
   fmt.Println("test02")
}

打印结果

test01

test02

foo function调用

13 defer语句

defer 应用场景

package main

import (
   "fmt"
   "os"
)

func main() {
   //打开文件
   fileObj,err:=os.Open("满江红")
   defer fileObj.Close()
   if err!=nil {
      fmt.Println("文件打开失败,错误原因",err)
   }
   fmt.Println(fileObj)
}

多个defer应用

package main

import (
   "fmt"
)

func foo() {
   fmt.Println("foo function调用")
}

func bar() {
   fmt.Println("bar function调用")
}

func main() {
   fmt.Println("test01")
   defer foo() //先注册的后调用
   fmt.Println("test02")
   defer bar() //延迟调用
}

defer的拷贝机制

案例1

函数注册的是谁,连参数一块儿保存下来

package main

import "fmt"

func main() {
   //(3) defer的拷贝机制
   foo := func() {
      fmt.Println("I am function foo1")
   }
   defer foo()
   foo = func() {
      fmt.Println("I am function foo2")
   }
}

案例2

package main

import "fmt"

func main() {
   // 案例2
   x:=10
   defer func(a int) {
      fmt.Println(a)
   }(x)
   x++
}

案例3

package main

import "fmt"

func main() {
   // 案例3
   x:=10
   defer func() {
      fmt.Println(x)
   }()
   x++ //x=x+1
}

defer执行时机

//defer执行时机
//return 10
//(1) rval = 10
//defer执行
//(2) ret

闭包进阶-练习题

版本1 

package main

import "fmt"

func main() {
   //版本1
   var fn [10]func()

   for i:=0;i<len(fn);i++{
      // 没发生调用,第1次执行 fn[0]=func(){fmt.Println(i)}
      // 没发生调用,第2次执行 fn[1]=func(){fmt.Println(i)}
      // 没发生调用,第3次执行 fn[2]=func(){fmt.Println(i)}
      fn[i]=func(){
         fmt.Println(i)
      }
   }
   //for循环执行完,内存中有一块地址存储 func(){fmt.Println(i)} i=10
   for _,f:=range fn{
      f()
   }
}
打印结果
10个 10

版本2 把range循环换成三要素循环

package main

import "fmt"

func main() {
   //版本2
   var fn [10]func()
   for i:=0;i<len(fn);i++ {
      fn[i]=func(){
         fmt.Println(i)
      }
   }
   //for循环执行完,内存中func(){fmt.Println(i)} i=10
   //注意i=10是上边for循环中的局部i
   for i:=0;i<len(fn);i++ {
      //每次都调用(局部i=10) func(){fmt.Println(i)} i=10
      fn[i]()
   }
}
打印结果
10个 10

版本3 把i换成j

package main

import "fmt"

func main() {
   //版本3
   var fn [10]func()
   for i:=0;i<len(fn);i++ {
      fn[i]=func(){
         fmt.Println(i)
      }
   }
   //for循环执行完,内存中func(){fmt.Println(i)} i=10
   //注意i=10是上边for循环中的局部i
   for j:=0;j<len(fn);j++ {
      //每次都调用(局部i=10) func(){fmt.Println(i)} i=10
      fn[j]()
   }
}
打印结果
10个 10

版本4 

//版本4
   var fn [10]func()
   var i int
   for i=0;i<len(fn);i++ {
      fn[i]=func(){
         fmt.Println(i)
      }
   }
   fmt.Println("----",i)

   //for循环执行完,内存中func(){fmt.Println(i)} i=10
   //这里i=10是var i int声明的i 下边for循环中的i 也是同一个i
   for i=0;i<len(fn);i++ {
   //for i=0;i<5;i++ { //更换为 i<5 打印 0 1 2 3 4 
      fn[i]()
   }
   fmt.Println("====",i)
}

版本4 理解 代码段

var i int
   for i=0;i<10;i++{
      fmt.Println("for",i)
   }
   fmt.Println("main",i)
   for i=0;i<5;i++{
      fmt.Println("for",i)
   }
   fmt.Println("main2",i)
}
打印结果
for 0
for 1
for 2
for 3
for 4
for 5
for 6
for 7
for 8
for 9
main 10
for 0
for 1
for 2
for 3
for 4
main2 5

版本4 帮助理解的代码段2

//版本4
   var fn [10]func()
   var i int
   for i=0;i<len(fn);i++ {
      fn[i]=func(){
         fmt.Println(i)
      }
   }
   fmt.Println("----",i)

   for i=0;i<5;i++ {
      fn[i]()
   }
   fmt.Println("====",i)
}
打印结果
---- 10
0
1
2
3
4
==== 5

版本5

package main

import "fmt"

func makeFun(i int)func() {
   return func(){
      fmt.Println(i)
   }
}

func main() {
   //版本5
   var fn [10]func()
   for i:=0;i<len(fn);i++ {
      fn[i]=makeFun(i)
   }

   for _,f:=range fn{
      f()
   }
}

版本6

 

package main

import "fmt"

func makeFun2()func(i int){
   return func(i int){
      fmt.Println(i)
   }
}

func main() {
   //版本6
   var fn [10]func(int)
   for i:=0;i<len(fn);i++ {
      fn[i]=makeFun2()
   }

   for i,f:=range fn{
      f(i)
   }
}
打印结果
0
1
2
3
4
5
6
7
8
9

 

上一篇:JAVA - 死锁


下一篇:Day05 Go语言文件操作,结构体,构造函数,方法接收器,json序列化