乐趣区

Golang-学习笔记函数

函数

在 Go 语言中,函数可以分为具名函数和匿名函数,包级函数一般是具名函数,具名函数是匿名函数的一种特例。函数也可以看成是封装好一系列语句的代码块,用以提高代码的模块度和复用率。

Go 语言的函数属于“一等值,(first-class)”,满足以下特点:

  • 函数本身可以被当成值传递
  • 函数本身可以满足接口
  • 支持匿名函数和闭包

函数调用

需求:定义两个函数,实现两数相加和两数相乘的功能,允许被 main 函数外部调用。

// 定义一个小马函数实现两数相加功能
func Xiaoma(a int,b int)int{
   sum := a + b
   fmt.Println("小马 a +b:",sum)
   return sum // 当有返回值语句时,谁调用就返回给谁
}
// 定义一个佳豪函数实现两数相乘功能
func Jiahao(a int,b int)  { // 没有返回值
   b += 1
   sumAdd := a * b
   fmt.Println("佳豪 a *b:",sumAdd)
}

func main(){
   a := 2
   b := 3
   // 打印初始值
   fmt.Printf("a = %d\n",a)
   fmt.Printf("b = %d\n",b)

   // 调用小马
   sum := Xiaoma(a,b)  // 传参
   fmt.Println("a+b:", sum) // 接受返回值

   // 调用佳豪
   Jiahao(a,b) // 传参
   fmt.Println("a*b:", a*b)
}

/*
a = 2
b = 3
小马 a +b: 5
a+b: 5
佳豪 a *b: 8
a*b: 6
*/

可以说明,当一个函数具有返回值时,就将返回值返回给调用的函数 。例如,Xiaoma 函数的返回值是 5,返回值“ 原路返回”以后,主函数就接收到了返回值 sum,最后打印输出 sum,为 5;当一个函数没有返回值时,就什么都不做。例如,Jiahao 函数没有返回值,函数顺序执行,先打印 Jiahao 函数体的输出 ab =8,接着再打印主函数的 a b =6。

调用机制

(此处用 Process.com 画一个图)

根据上述代码块可以得知 Go 语言的函数调用具有以下特点

  • 在调用函数时,内存会为该函数分配一个栈空间,编译器会让不同函数之间都能区分开来。
  • 在每个函数对应的栈空间中,数据空间是独立的,不会混淆
  • 当函数调用执行完毕后,程序会回收函数所在的栈空间,释放内存资源
  • 如果有返回值语句,就将返回值返回给调用的函数(语句);如果没有返回值,就什么都不做

返回值的声明形式

返回值就是函数执行后返回的内容,Go 语言的函数允许有 单个 多个 甚至是 没有 返回值。

  • 如果函数有返回值,必须要在函数最后添加 return 语句;如果函数没有返回值,那么就省略最后的返回信息,什么都不用写。
  • 如果函数只有一个返回值,且不声明返回值变量,那么可以省略这个返回值不写

    func max (a,b int)(maxNum int){// 形参列表 a 和 b 都是 int 型,可以合并;返回值只有一个 maxNum,且
        if a > b {return a}
        return b
    }
  • 如果函数是多返回值函数,如果不声明变量,也要指定其返回值类型。(以下两种声明方式等价)

    // 声明多返回值类型时形参类型一样可以合并,需要指定其返回值类型,可以省略变量
    func Cat (A, B int)(int, int){return A + b, A * B}
    // 另外一种多返回值函数的声明形式,指定返回值变量及其类型
    func Cat(A, B int)(Sum int, SumAdd int){
        Sum := A + B
        SumAdd := A * B
        return Sum, SumAdd
    }

【注】:私有函数名一般使用小写开头,公有函数名一般使用大写开头。两者之间的区别在于是否能被外部调用。

多返回值函数调用

需求:定义一个可以实现四则运算的函数,拥有多个返回值,允许被 main 函数外部调用

// 定义一个能够求矩形周长和面积的函数
func  Rectangle(a, b int)(d int,s int){d := 2*(a+b)
    s := a*b
    return d, s  // 返回值
}
// 主函数
func main (){
    a := 3
    b := 4
    dog, cat := Rectangle(a,b) // 传参
    // 打印初始值
    fmt.Println("a 的初始值:",a)
    fmt.Println("b 的初始值:",b)
    // 打印结果
    fmt.Printf("矩形周长 %d\n", dog)
    fmt.Printf("矩形面积 %d\n", cat)
}

/*
a 的初始值: 3
b 的初始值:4
矩形周长 14
矩形面积 12
*/

但是,多返回值也意味着有时候并不是全部都需要,我们可以使用空标识符 “_” 忽略函数返回值。还是以上面的代码为例,只需要 Rectangle 函数实现求周长的功能,不需要求出面积。

// 只需要 Rectangle 函数实现求周长的功能,不需要求出面积
func  Rectangle(a, b int)(int, int){d := 2*(a+b)
    s := a*b
    return  d, s 
}
// 主函数
func main (){
    a := 3
    b := 4
    dog, _ := Rectangle(a, b) // 将第一个返回值 d 赋值给 dog, 返回值 s 赋值给空标识符, 然后自动丢弃
    // 打印初始值
    fmt.Println("a 的初始值:",a)
    fmt.Println("b 的初始值:",b)
    // 打印结果
    fmt.Printf("矩形周长 %d\n", dog)
    //fmt.Printf("矩形面积 %d\n", cat) // 编译报错,因为 cat 参数已被忽略
}

/*
a 的初始值: 3
b 的初始值:4
矩形周长 14
*/

函数作为参数

刚刚在本文前言已经提到了,函数作为“一等值”,是可以作为参数传递的,然后可以在其他函数内部调用执行,称为“回调”。只要求被调用函数的 返回值个数 返回值类型 返回值顺序 与调用函数所需的 实参 是一致的,就可以把这个被调用的函数当作其他函数的参数。

(代码待补充,暂时理解不了)

函数作为类型

(施工中)

可变参数

(施工中)

匿名函数与闭包

匿名函数是指不需要定义函数名的一种函数定义方式。当一个函数声明没有命名函数名称时,不能够独立存在;但可以被赋值于某个变量,即保存函数的地址到变量中

package main

import "fmt"

func main() {f() // 调用匿名函数
}
func f() {
    for i := 0; i < 4; i++ {g := func(i int) {fmt.Printf("%d", i) // 循环打印
        }
        g(i) //
        fmt.Printf("- g is of type %T and has value %v\n", g, g)
    }
}

/*
0  - g is of type func(int) and has value 0x49c220
1  - g is of type func(int) and has value 0x49c220
2  - g is of type func(int) and has value 0x49c220
3  - g is of type func(int) and has value 0x49c220
*/
退出移动版