美文网首页golang
Go语言学习笔记07.函数

Go语言学习笔记07.函数

作者: 快乐的提千万 | 来源:发表于2019-11-05 16:44 被阅读0次

基本格式

func FuncName(/*参数列表*/) (o1 type1, o2 type2/*返回类型*/) {
    //函数体

    return v1, v2 //返回多个值
}

函数名首字母小写即为private,大写即为public,也就是如果你的函数要跨文件调用,需要首字母大写。
不支持默认参数
返回类型:
① 上面返回值声明了两个变量名o1和o2(命名返回参数),这个不是必须,可以只有类型没有变量名
② 如果只有一个返回值且不声明返回值变量,那么你可以省略,包括返回值的括号
③ 如果没有返回值,那么就直接省略最后的返回信息
④ 如果有返回值, 那么必须在函数的内部添加return语句

参数

package main //必须

import "fmt"

//无参无返回值函数的定义
func MyFunc() {
    a := 666
    fmt.Println("a = ", a)
}

//有参无返回值函数的定义
//定义函数时, 在函数名后面()定义的参数叫形参
//参数传递,只能由实参传递给形参,不能反过来, 单向传递
func MyFunc01(a int) {
    fmt.Println("a = ", a)
}

//多个参数
func MyFunc03(a, b int) {
    fmt.Printf("a = %d, b = %d\n", a, b)
}
func MyFunc04(a int, b string, c float64) {
}
func MyFunc05(a, b string, c float64, d, e int) {
}
func MyFunc06(a string, b string, c float64, d int, e int) {
}

//...int类型这样的类型, ...type不定参数类型
//注意:不定参数,一定(只能)放在形参中的最后一个参数
func MyFunc02(args ...int) { //传递的实参可以是0或多个
    fmt.Println("+++++++++++++++++++++++++++++++++++++++++++++++")
    fmt.Println("len(args) = ", len(args)) //获取用户传递参数的个数
    for i := 0; i < len(args); i++ {
        fmt.Printf("args[%d] = %d\n", i, args[i])
    }

    fmt.Println("==========================================")

    //返回2个值,第一个是下标,第二个是下标所对应的数
    for i, data := range args {
        fmt.Printf("args[%d] = %d\n", i, data)
    }
    fmt.Println("+++++++++++++++++++++++++++++++++++++++++++++++")
}

//传递部分参数
func test(args ...int) {
    //全部元素传递给myfunc
    //myfunc(args...)

    //只想把后2个参数传递给另外一个函数使用
    MyFunc02(args[:2]...) //args[0]~args[2](不包括数字args[2]), 传递过去
    MyFunc02(args[2:]...) //从args[2]开始(包括本身),把后面所有元素传递过去
}

func main() {
    //无参无返回值函数的调用: 函数名()
    MyFunc()

    //有参无返回值函数调用:  函数名(所需参数)
    //调用函数传递的参数叫实参
    MyFunc01(666)

    MyFunc02(1,2,3,4,5)
    
    test(1, 2, 3, 4)
}

返回值

package main //必须

import "fmt"

//无参有返回值:只有一个返回值
//有返回值的函数需要通过return中断函数,通过return返回
func myfunc01() int {
    return 666
}

//给返回值起一个变量名,go推荐写法
func myfunc02() (result int) {

    return 666
}

//给返回值起一个变量名,go推荐写法
//常用写法
func myfunc03() (result int) {

    result = 666
    return
}

//多个返回值
func myfunc011() (int, int, int) {
    return 1, 2, 3
}

//go官方推荐写法
func myfunc022() (a int, b int, c int) {
    a, b, c = 111, 222, 333
    return
}

func myfunc033() (a, b, c int) {
    a, b, c = 111, 222, 333
    return
}


func main() {
    //无参有返回值函数调用
    var a int
    a = myfunc01()
    fmt.Println("a = ", a)

    b := myfunc01()
    fmt.Println("b = ", b)

    c := myfunc03()
    fmt.Println("c = ", c)

    a, b, c = myfunc022()
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

关于函数调用顺序

和C一样,是栈的形式。被调用者先执行。
比如ABC三个函数,调用顺序为C-->B-->A,则A会最先执行。
同样,递归就是自己调用自己,假设3-->2-->1,则会最先输出1。

函数类型

将函数看成一种数据类型, 通过type给一个函数类型起名,然后可以赋值,但是参数和返回值需要一样,类似于重写(同名覆盖)。
回调函数:函数有一个参数是函数类型,这个函数就是回调函数。更准确地说是将一个函数的指针作为参数传递给另一个函数。而回调函数的定义则是不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

package main //必须

import "fmt"

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

func Minus(a, b int) int {
    return a - b
}

//FuncType它是一个函数类型
type FuncType func(int, int) int //没有函数名字,没有{}

//回调函数,函数有一个参数是函数类型,这个函数就是回调函数
//计算器,可以进行四则运算
//多态,多种形态,调用同一个接口,不同的表现,可以实现不同表现,加减乘除
//现有想法,后面再实现功能
func Calc(a, b int, fTest FuncType) (result int) {
    fmt.Println("Calc")
    result = fTest(a, b) //这个函数还没有实现
    //result = Add(a, b) //Add()必须先定义后,才能调用
    return
}

func main() {
    
}
func main() {
    var result int
    result = Add(1, 1) //传统调用方式
    fmt.Println("result = ", result)

    //声明一个函数类型的变量,变量名叫fTest
    var fTest FuncType
    fTest = Add            //是变量就可以赋值
    result = fTest(10, 20) //等价于Add(10, 20)
    fmt.Println("result2 = ", result)

    fTest = Minus
    result = fTest(10, 5) //等价于Minus(10, 5)
    fmt.Println("result3 = ", result)
}

匿名函数和闭包

没有函数名字的就是匿名函数。

    a := 10
    //匿名函数,没有函数名字, 函数定义,还没有调用
    f1 := func() { //:= 自动推导类型
        a = 100  //注意这里会修改外部变量的值
        fmt.Println("a = ", a) //a= 100
    }
    f1()
    fmt.Println("a = ", a)//a= 100
    
    //定义匿名函数,同时调用
    func() {
        fmt.Println("a = ", a) //a= 100
    }() //后面的()代表调用此匿名函数
    
    //带参数的匿名函数
    f3 := func(i, j int) {
        fmt.Printf("i = %d, j = %d\n", i, j)
    }
    f3(1, 2)

    //定义匿名函数,同时调用
    func(i, j int) {
        fmt.Printf("i = %d, j = %d\n", i, j)
    }(10, 20)

闭包,要理解起来会麻烦一点。首先我们要知道几个概念,当函数调用结束后,所有局部变量会释放,除了返回值。函数A中定义了函数B,则B外面的叫外函数,里面的叫内函数,假如外函数有有个临时变量a,内函数有个临时变量B。
此时,内函数使用了a,且函数A的返回值是函数B的引用,则叫闭包。
比如:

import "fmt"

//函数A
func add_function_generator(i, j int) int {
    //这里是外函数
    
    //函数B,也叫闭包
    a := func(i, j int) (sum int) {
        //这里是内函数
        sum = i + j
        return 
    }(i,j)
    return a
}

func main() {
    a := add_function_generator(1,1)
    fmt.Println("a = ", a)
}

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
比如:

package main //必须

import "fmt"

//函数的返回值是一个匿名函数,返回一个函数类型
//该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
    //函数被调用时,x才分配空间,才初始化为0
    var x int 
    return func() int {
        x++
        return x * x
    }
}

func test(){
    f := squares()
    fmt.Println(f()) //1
    fmt.Println(f()) //4
    fmt.Println(f()) //9
    fmt.Println(f()) //16
    fmt.Println(f()) //25
    //在这里squares()里面的x才释放
}

//一般函数,函数结束了就释放局部变量
func test01() int {
    var x int //没有初始化,值为0
    x++
    return x * x //函数调用完毕,x自动释放
}

func main() {

    //返回值为一个匿名函数,返回一个函数类型,通过f来调用返回的匿名函数,f来调用闭包函数
    //它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。
    test()
    f := squares()
    fmt.Println(f()) //1

    fmt.Println(test01())//1
    fmt.Println(test01())//1
    fmt.Println(test01())//1
    fmt.Println(test01())//1
}

其实本质来说,就跟宏替换一样,把闭包里面的函数体直接替换到外函数就成了这样:

func test(){
    var res int //这个变量原来会由系统生成,记录返回值

    var x int 
    
    x++
    res =  x * x
    fmt.Println(res) 

    x++
    res =  x * x
    fmt.Println(res) 

    x++
    res =  x * x
    fmt.Println(res) 

    x++
    res =  x * x
    fmt.Println(res) 
    
    //在这里x才释放
}

作用域

函数内定义的变量称为局部变量。
函数外定义的变量称为全局变量。
在不同作用域可以声明同名的变量,其访问原则为:在同一个作用域内,就近原则访问最近的变量,如果此作用域没有此变量声明,则访问全局变量,如果全局变量也没有,则报错。

package main //必须

import "fmt"

var a byte //全局变量

func main() {
    var a int //局部变量

    //1、不同作用域,允许定义同名变量
    //2、使用变量的原则,就近原则
    fmt.Printf("1: %T\n", a) //int

    {
        var a float32
        fmt.Printf("2: %T\n", a) //float32
    }
    
    test()
}

func test() {
    fmt.Printf("3: %T\n", a) //uint8, 就是byte
}

相关文章

  • Go语言学习笔记07.函数

    基本格式 函数名首字母小写即为private,大写即为public,也就是如果你的函数要跨文件调用,需要首字母大写...

  • go 零散笔记(未整理)

    go 学习零散笔记 Go语言程序员推荐使用 驼峰式 命名 函数(func) 一个函数的声明由func关键字、函数名...

  • 笨办法学golang(三)

    这是Go语言学习笔记第三篇。 Go语言学习笔记参考书籍「Go语言圣经」以及Go官方标准库 Go语言基本类型主要有布...

  • 笨办法学golang(二)

    这是Go语言学习笔记的第二篇文章。 Go语言学习笔记参考书籍「Go语言编程」、Go官方标准库 前文提要 上篇文章中...

  • 笨办法学golang(四)

    这是Go语言学习笔记的第四篇 Go语言学习笔记参考书籍「Go语言圣经」以及Go官方标准库 数组 数组是指一系列同类...

  • Go语言学习笔记 - 函数

    函数 不支持 嵌套 (nested)、重载 (overload) 和 默认参数 (default paramete...

  • Go语言学习笔记-函数

    函数 可以有多个返回值 所有参数都是值传递,slice,map,channel会有传引用的错觉 函数可以作为变量的...

  • Go 语言极速入门

    本系列文章主要是记录《Go 语言实战》和《Google 资深工程师深度讲解 Go 语言》的学习笔记。 Go 语言极...

  • (四)go语言函数&参数传递

    go语言函数 基本语法 例子 go语言参数传递

  • Golang相关电子书

    Golang相关电子书,会不定期更新 Go语言程序设计 Go语言学习笔记.雨痕(详细书签) Go语言·云动力(云计...

网友评论

    本文标题:Go语言学习笔记07.函数

    本文链接:https://www.haomeiwen.com/subject/ykkebctx.html