zoukankan      html  css  js  c++  java
  • GO函数

    函数定义

    Go语言中定义函数使用func关键字。

    func 函数名(参数)(返回值){
        函数体
    }
    

    函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
    参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
    返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。
    函数体:实现指定功能的代码块。

    函数定义及调用

    //定义一个不需要参数也没有返回值的函数
    func say(){
        fmt.Println("Hello!")
    }
    //定义个接受string类型的name参数
    func say2(name string)  {
        fmt.Println("hello ",name)
    }
    func main(){
        //函数调用
        say()   //Hello!
        say2("Ares")    //hello  Ares
    }
    

    参数

    参数简写

    函数的参数中如果相邻变量的类型相同,则可以省略类型

    func intsum(x ,y int)  {
        //return x + y
        fmt.Println(x+y)
    }
    

    intSum函数有两个参数,这两个参数的类型均为int,因此可以省略x的类型.

    可变参数

    可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...来标识。

    func intsum2(x ... int) int {
        fmt.Println(x)
        sum := 0
        for _,v :=range x{
            sum += v
        }
        return sum
    }
    
    func main(){
        //函数调用
        ret1 := intsum2()   //[]
        ret2 := intsum2(2)  //[2]
        ret3 := intsum2(3,3,3,3,3)  //[3 3 3 3 3]
        fmt.Println(ret1,ret2,ret3)     //0 2 15
    }
    

    固定参数搭配可变参数时,可变参数要放固定参数后面。

    func intsum3(x int,y ... int)int  {
        fmt.Println(x,y)
        sum := x
        for _,v :=range y{
            sum += v
        }
        return sum
    }
    
    func main(){
        //函数调用
        ret4 := intsum3(10)     //10 []
        ret5 := intsum3(10,10)  //10 [10]
        ret6 := intsum3(10,11,12,13)    //10 [11 12 13]
        fmt.Println(ret4,ret5,ret6)     //10 20 46
    }
    

    函数的可变参数是通过切片来实现的!

    多返回值

    函数如果有多个返回值时必须用()将所有返回值包裹起来。

    //多返回值
    func many(x,y int)(int, int)  {
        sum := x+y
        sub := x-y
        return sum,sub
    }
    
    func main(){
        //函数调用
        ret7,ret8 := many(2,3)
        fmt.Println(ret7,ret8)  // 5  -1
    }
    

    函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。return后的数据必须和函数定义的一致:个数、类型、顺序。
    可以使用"_"来舍弃多余的返回值。
    函数没有定义返回值也可使用return,用来结束函数执行(之后代码不再执行).

    //多返回值命名
    func many1(x,y int)(sum,sub int)  {
        sum = x+y
        sub = x-y
        return
    }
    func main(){
        //函数调用
        ret7,ret8 := many1(2,3)
        fmt.Println(ret7,ret8)  // 5  -1
    }
    

    defer语句

    Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。先进后出,后进先出。

    func main(){
        fmt.Println("start")
        defer fmt.Println(1)
        defer fmt.Println(2)
        defer fmt.Println(3)
        fmt.Println("end")
    }
    输出:
    start
    end
    3
    2
    1
    

    defer语句能非常方便的处理资源释放问题!

    函数进阶

    全局变量

    全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。

    //定义全局变量
    var num int = 10
    
    func test()  {
        fmt.Printf("num=%d
    ",num)
    }
    func main()  {
        test()  //num=10
    }
    

    局部变量

    如果局部变量和全局变量重名,优先访问局部变量!

    //定义全局变量
    var num int = 10
    
    //函数内定义局部变量
    func test1()  {
        var num1 int =  20
        fmt.Printf("num=%d,num1=%d
    ",num,num1)
    }
    //语句块内定义
    func test2(x,y int)  {
        fmt.Println(x,y)    //函数参数只在本函数中有效 2 1
        if x > y{
            z := 1      //z只在if语句中有效
            fmt.Println(z)  //1
        }
    }
    func main()  {
        test1() //num=10,num1=20
        test2(2,1)
    }
    

    递归函数

    一个函数自己调用自己,就是递归函数。

    //计算1+2+...+n的值
    func sum(n int) int {
        if n == 1 {
            return 1
        }
        return sum(n-1) + n
    }
    
    //斐波那契数列(Fibonacci sequence)
    func fei(n int) int {
        if n == 1 || n == 2 {
            return 1
        }
        return fei(n-1) + fei(n-2)
    }
    func main() {
        fmt.Println(sum(5))
        fmt.Println(fei(12))
    }
    

    函数作为变量

    func test()  {
        fmt.Printf("num=%d
    ",num)
    }
    func main()  {
        aa := test //将函数test内存地址赋值给aa,不能使用test(),test()是调用函数代码,将返回值赋值给aa
        fmt.Printf("%T
    ",aa)   //func()
        aa()    //num=10
    }
    

    函数作为参数(回调函数)

    //函数作为参数
    func add(x,y int) int {
        return x + y
    }
    func sub(x,y int)int  {
        return x-y
    }
    //op是函数名,接受两个int类型参数,返回一个int类型
    func calc(x,y int,op func(int,int) int) int  {
        return op(x,y)
    }
    func main()  {
        ret := calc(10,20,add)
        fmt.Println(ret)    //30
        ret1 := calc(10,20,sub)
        fmt.Println(ret1)   //-10
    }
    

    参数传递

    值传递:传递的是数据的副本、修改数据,对源数据没有影响,值类型的数据,默认都是值传递

    引用传递:传递的是数据的内存地址,多个变量指向同一块内存,引用类型的数据,默认都是引用传递。

    func zhichuandi(arr2 [3]int) {
        fmt.Println("函数中,数组数据:", arr2) //[1 2 3]
        arr2[0] = 100
        fmt.Println("函数中,数组数据更改后:", arr2) //[100 2 3]
    }
    
    func yinyong(arr4 []int) {
        fmt.Println("函数中,切片数组:", arr4) //[1 2 3]
        arr4[0] = 100
        fmt.Println("函数中,切片更改后:", arr4) //[100 2 3]
    }
    
    func main() {
        arr1 := [3]int{1, 2, 3}
        fmt.Println("函数调用前,数组数据:", arr1) //[1 2 3]
        zhichuandi(arr1)
        fmt.Println("函数调用后,数组数据:", arr1) //[1 2 3]
        fmt.Println("------------------")
        arr3 := []int{1, 2, 3}
        fmt.Println("函数调用前,切片数据:", arr3) //[1 2 3]
        yinyong(arr3)
        fmt.Println("函数调用后,切片数据:", arr3) //[100 2 3]
    }
    

    匿名函数

    匿名函数就是没有函数名的函数,匿名函数多用于实现回调函数和闭包。
    匿名函数直接在函数后使用()调用,通常只能使用一次,可以使用匿名函数赋值给变量,可以调用多次。
    将匿名函数作为另一个函数的参数,即为回调函数。
    将匿名函数作为另一个函数的返回值,形成闭包。
    格式:

    func(参数)(返回值){
        函数体
    }
    

    定义完匿名函数后直接执行:

    func main()  {
    //方式1
        func(x,y int){
            fmt.Println(x + y)
        }(1,2)  //3,定义完匿名函数后加()直接执行
    }
    //方式2
    say := func(){
            fmt.Println("匿名函数") 
        }
        say()   //匿名函数
    

    匿名函数使用方式

    在定义匿名函数时就直接调用

    //定义匿名函数的同时调用
    ret1 := func(n1,n2 int) int {
        return n1 + n2
    }(1,2) //将执行结果返回给ret1
    fmt.Println("ret1=",ret1)       //ret1= 3
    

    将匿名函数赋给一个变量(函数变量),再通过变量来调用匿名函数

    //把匿名函数赋值给a变量
    a := func(n1,n2 int) int {
        return n1 + n2
    } //将函数地址赋值给a
    //通过a来调用匿名函数
    ret2 := a(1,3)
    fmt.Println("ret2=",ret2)  //ret2= 4
    

    全局匿名函数

    //把全局匿名函数赋值给一个全局变量
    var (
        //全局匿名函数 赋值给变量fun1
        Fun1 = func(n1 int, n2 int) int {
            return n1 * n2
        }
    )
    func main()  {
        fmt.Println(Fun1(1,2))  //2
    }
    

    闭包

    闭包=函数+引用环境
    一个外层函数中,有内层函数,该内存函数中,会操作外层函数的局部变量(外层函数中的参数,或者外层函数中直接定义的变量),并且该外层函数的返回值就是这个内层函数。
    这个内层函数和外层函数的局部变量,统称为闭包。
    只要引用到外部变量的,就是闭包!

    //闭包
    //定义一个函数返回值是一个函数
    func a() func() {
        name := "ares"
        return func(){
            fmt.Println("hello ",name)
        }
    }
    //方式2
    func b(name string) func() {
        return func(){
            fmt.Println("hello ",name)
        }
    }
    func main()  {
        r := a()   //此时r就是一个闭包
        r() //相当于执行了a函数背部的匿名函数
    }
    r2 := b("ares")
    r2()
    

    使用闭包做文件名后缀检测实例:

    func makeSuffixFunc(suffix string) func(string) string {
        return func(name string) string {
            //若文件后缀是不是以suffix结尾
            if !strings.HasSuffix(name, suffix) {
                //如果不是,返回name + suffix
                return name + suffix
            }else{
                //如果是,返回ok
                return "ok"
            }
            return name
        }
    }
    func main()  {
        jpgFunc := makeSuffixFunc(".jpg")
        txtFunc := makeSuffixFunc(".txt")
        fmt.Println(jpgFunc("test")) //test.jpg
        fmt.Println(txtFunc("test.txt")) //ok
    }
    

    闭包实例:

    func calc(base int) (func(int) int, func(int) int) {
        add := func(i int) int {
            base += i
            return base
        }
    
        sub := func(i int) int {
            base -= i
            return base
        }
        return add, sub
    }
    func main()  {
        x,y := calc(10)
        ret1 := x(10)   //base=10+10=20
        fmt.Println(ret1)   //20
        ret2 := y(30)   //base=20-30
        fmt.Println(ret2)   //-10
    }
    

    内置函数

    panic/recover

    GO可以使用panic/recover模式来处理错误。 panic可以在任何地方引发,但recover只有在defer调用的函数中有效。
    实例:

    func a()  {
        fmt.Println("func a")
    }
    func b()  {
        panic("func b")
    }
    func c()  {
        fmt.Println("func c")
    }
    func main()  {
        a()
        b()
        c()
    }
    

    执行到func b的时候会抛出异常,程序崩溃,可以使用recover将程序恢复

    func a()  {
        fmt.Println("func a")
    }
    func b()  {
        defer func() {
            err := recover()
            if err != nil{
                fmt.Println("recover fun b")
            }
        }()
        panic("func b")
    }
    func c()  {
        fmt.Println("func c")
    }
    func main()  {
        a() //func a
        b() //recover fun b
        c() //func c
    }
    
    • recover()必须搭配defer使用。
    • defer一定要在可能引发panic的语句之前定义。
  • 相关阅读:
    Javascript一天学完系列(四)函数上下文bind &call
    Javascript一天学完系列(三)JavaScript面向对象
    Javascript一天学完系列(二)Callbacks回调函数
    Python(切片)
    水果篮子(母函数)
    判断链表是否有环
    链表部分逆置
    Python(List和Tuple类型)
    HDU1426(DFS)
    HDU4474(数位BFS)
  • 原文地址:https://www.cnblogs.com/aresxin/p/GO-han-shu.html
Copyright © 2011-2022 走看看