大家晚上好!从今天开始接下来的学习任务内容会上升一个高度嗷

第7天的学习从函数开始,在Go语言中函数的定义如下:

func functionName([parameter list]) [returnTypes]{ //body }

  • 函数由func关键字进行声明。
  • functionName:代表函数名。
  • parameter list:代表参数列表,函数的参数是可选的,可以包含参数也可以不包含参数。
  • returnTypes:返回值类型,返回值是可选的,可以有返回值,也可以没有返回值。
  • body:用于写函数的具体逻辑 我们来看一个简单的求和函数:
func getSum(n1 int,n2 int) int {
    return n1 + n2
}

这个函数传递了两个参数,分别为num1与num2,并且他们都为int类型,将相加后的结果进行返回。 同时也支持这样的参数声明:

func getSum(num1, num2 int) int { //省略第一个参数的类型,Go编译器会自动推导
    return n1 + n2
}

golang中函数也是一种数据类型,可以赋值给变量。还是上面的求和函数:

func getSum(n1 int,n2 int) int {
    return n1+n2
}
func main(){
    a := getSum
    //a, getSum的类型此时都是fun(int,int) int
    fmt.Println("a的类型是:%T, getSum的类型是:%T", a, getSum)
    a(10, 40) //等价于 getSum(10,40)
}

函数也可以作为形参,并且调用:

func getSum(n1 int, n2 int) int {
    return n1 + n2
}

//再写一个函数,接收函数作为参数
func funtest(funvar fun(int, int) int , num1 int, num2 int)
    return funvar(num1, num2)

func main(){
    res:=funtest(getSum,10,40)//从上面代码知道,getSum的类型是fun(int,int) int,将其传递给
                              //funvar变量,那么它就会调用getSum实现两数相加
}

Go支持自定义数据类型

理解

可以理解是取别名,但是在Go中还是不同的类型,仅仅是取别名而已

语法

Type 自定义名 基本数据类型

举例

Type myfun func(int, int) int

Type myFun func(int,int) int //此时myFun等价于一个函数类型func(int,int) int
Type myint int
var num1 int
var num2 myint
num1 = num2   //这里会报错,因为根本上两者是不同的类型,不可以赋值,可以使用类型转换解决

GO支持对函数返回值命名

func getSum(n1 int,n2 int) (sum int, sub int) {
    sum = n1 + n2
    sub = n1 - n2
    return  //这里就可以不用写成 return sum,sub
}

Go可以使用 "_"来忽略返回值

Go中的init函数----注意执行顺序

Go中的匿名函数

匿名函数如其名字一样,是一个没有名字的函数,除了没有名字外其他地方与正常函数相同。匿名函数可以直接调用,保存到变量,作为参数或者返回值。

//匿名函数可以直接写在main()中,不需要在main()中定义
fuc main(){
  res := func (n1 int, n2 int) int {
        return n1 + n2
}(10, 30) //这里声明的时候同时可以直接调用

Go中的全局匿名函数

package main

var (
  myfunc := func (n1 int, n2 int) int {   
      return n1 + n2
}
)

func main(){
  res:=myfunc(10,20)
}

Go中的闭包初识

闭包可以解释为一个函数与这个函数外部变量的一个封装。粗略的可以理解为一个类,类里面有变量和方法,其中闭包所包含的外部变量对应着类中的静态变量。

//返回的是一个匿名函数,但是这个匿名函数引用到了函数外的变量n;
//但是这个匿名函数会和这个n形成一个引用关系的整体,构成闭包
func Add() func (int) int {  //Add()函数是返回了一个匿名函数
  var n int = 10
  return func (x int) int {
        n = n + x
      return n
  }
}

func main(){
  //首先返回一个闭包
  f := Add()   //f变量接收Addupper返回的函数

  //调用闭包 
  fmt.Println(f(1))// 11=10+1
  fmt.Println(f(2))// 13=11+2
  fmt.Println(f(3))// 16=13+3
  //此时发现在第一次计算后n的值并没有被重新初始化为10,而是使用了11,这就说明使用闭包的时候
  //会保存函数外的那个变量值(引用值)
  //当反复调用f函数时,因为n是只初始化一次,因此每次调用一次就进行累计,而不会初始化。
  //变量n永远隐式存在于f中
}

案例比较

//①编写一个函数makeSuffix,可以接收一个文件后缀名(比如.jpg),并返回一个闭包
//②调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回.jpg,如果有则全称
//③要求使用闭包方式
//④strings.HasSuffix该函数可以来判断某个字符串是否有指定后缀
func makesuffix(suffix string) func (string) string{    
      return func (name string) string  {        
            if !strings.HasSuffix(name,suffix) {         
                   return name+suffix      
        }       
       return name           
       }
}

func main(){
    f:=makesuffix(".jpg")//先返回一个闭包
    fmt.Println(f("abc"))//调用闭包
}

//使用普通函数
func makesuffix(name string, suffix string) string {
  if !strings.HasSuffix(name, suffix) {
      return name + suffix
   }
      return name
}

func main(){
  f1:=makesuffix("abc","jpg")
  f2:=makesuffix("cmdjpg","jpg")
  fmt.Println(f)
}

闭包函数只需要传递一次要测试的jpg后缀;用普通函数的话,传递完成后缀会消失,使用闭包则会保存,可以反复使用。如上面讲的Add( )函数会保存函数外的那个n值。

Go函数参数的传递方式

1.值传递 2.引用传递 另外,不管是值传递还是引用传递,本质上都是变量的副本,不同的是,值传递的是值的的拷贝,而引用传递的是地址的拷贝,所以引用传递在效果上是可以改变函数外原来变量的值。一般来说,地址拷贝效率更高,因为数据量小,值拷贝决定拷贝的数据大小,数据越大效率越低。

值类型和引用类型(补充)

值类型: 基本数据类型、数组、结构体

    1. 引用类型:指针、slice切片、map、管道channel、interface等都是引用类型

函数变量的作用域

  1. 局部变量
  2. 全局变量
  • 注意首字母大写的效果(大写时可在整个程序使用,不大写仅在本包使用) 3.遇到if/for循环时的变量作用域,注意在函数内部对全局变量的修改(区分使用赋值操作和重新定义声明操作) 注意:赋值语句如果不是使用var关键字,在函数外使用":="的时候是不允许的,见下图:

Go语言和其他很多语言一样有很多已定义好的函数(系统函数),比如字符串操作函数等等,这里就不再列出了,具体的函数定义与用法,大家可在Go官网查找学习,阅读源码也是一种很好的学习方法嗷!

参考资料 [1] https://github.com/datawhalechina/go-talent