跟我一起来学golang之《函数》(二)| 8月更文挑战

114 阅读5分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

函数是一块执行特定任务的代码。一个函数是在输入源基础上,通过执行一系列的算法,生成预期的输出。我们接着之前没讲完的函数模块继续学习。

空白标识符

_是Go中的空白标识符。它可以代替任何类型的任何值。让我们看看这个空白标识符的用法。

比如以下函数返回的结果是面积和周长,如果我们只要面积,不要周长,就可以使用空白标识符。

示例代码:

package main

import (  
    "fmt"
)

func rectProps(length, width float64) (float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}
func main() {  
    area, _ := rectProps(10.8, 5.6) // perimeter is discarded
    fmt.Printf("Area %f ", area)
}

变量的作用域

局部变量

一个函数内部定义的变量,就叫做局部变量

全局变量

一个函数外部定义的变量,就叫做全局变量

示例代码:

package main

import "fmt"
//n := 1000 //全局变量,不能省略var
var n = 1000
func main() {
    /*
    变量的作用域:
        1.局部变量和全局变量
            局部变量:函数内部,或者{}里定义的变量
                if,switch,for。。。{}
            全局变量:函数外定义的变量,叫全局变量。
                所有的函数都可以使用,而且是大家共享一份数据。
     */
    for i := 0; i < 3; i++ {
        fmt.Println("for....i", i)
    }

    j := 100
    fmt.Println(j)
    if j > 0 {
        fmt.Println(j) //100
        j := 200
        fmt.Println(j) // 200
    }
    fmt.Println(j) //100
    {
        //代码块
        fmt.Println(j)
        m := 300
        fmt.Println(m)
    }
    fun1()
    fun2()
    //fmt.Println(k)
}

func fun1()  {
    k := 1//函数内部定义的变量,局部变量,随着函数的调用而创建,随着函数的结束而销毁。
    fmt.Println(k)

    n = 2000
    fmt.Println(n) // 2000
}

func fun2(){
    //fmt.Println(k)
    fmt.Println(n) // 2000
}

递归函数

什么是递归函数

一个函数自己调用自己,就叫做递归调用

递归函数的出口

一个递归函数一定要有出口,否则会陷入死循环

示例代码:

package main

import "fmt"

func main()  {
    /*
    递归函数:一个函数自己调用自己
        递归函数都有出口。

     */
     r1:= getSum(5) // r1:= getSum(4)+5
     fmt.Println(r1)

     fmt.Println(jieCheng(5))

     fmt.Println(fibonacci(12))

}

func getSum(n int) int{
    fmt.Println("*********")
    if n == 1{
        return 1
    }
    return getSum(n-1) + n
}

 func jieCheng(n int) int{
     if n == 1{
         return 1
    }
    return jieCheng(n - 1) * n
 }

 func fibonacci(n int)int{
    if n == 1 ||n == 2{
        return 1
     }
     return fibonacci(n -1) + fibonacci(n-2)
 }

运行结果:

*********
*********
*********
*********
*********
15
120
144

defer函数

什么是延迟

即延迟(defer)语句,延迟语句被用于执行一个函数调用,在这个函数之前,延迟语句返回。

延迟函数

你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题

  • 如果有很多调用defer,那么defer是采用后进先出模式
  • 在离开所在的方法时,执行(报错的时候也会执行)
func ReadWrite() bool {
    file.Open("file")
    defer file.Close()
    if failureX {
          return false
    } i
    f failureY {
          return false
    } 
    return true
}

最后才执行file.Close()

示例代码:

package main

import "fmt"

func main() {
    a := 1
    b := 2
    defer fmt.Println(b)
    fmt.Println(a)
}

运行结果:

12

示例代码:

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

运行结果:

Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest

延迟方法

延迟并不仅仅局限于函数。延迟一个方法调用也是完全合法的。让我们编写一个小程序来测试这个。

示例代码:

package main

import (  
    "fmt"
)

type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}

运行结果:

Welcome John Smith

延迟参数

延迟函数的参数在执行延迟语句时被执行,而不是在执行实际的函数调用时执行。

让我们通过一个例子来理解这个问题。

示例代码:

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

运行结果:

value of a before deferred function call 10  
value of a in deferred function 5

堆栈的推迟

当一个函数有多个延迟调用时,它们被添加到一个堆栈中,并在Last In First Out(LIFO)后进先出的顺序中执行。

我们将编写一个小程序,它使用一堆defers打印一个字符串。示例代码:

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Orignal String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

运行结果:

Orignal String: Naveen  
Reversed String: neevaN

defer注意点

defer函数:
当外围函数中的语句正常执行完毕时,只有其中所有的延迟函数都执行完毕,外围函数才会真正的结束执行。
当执行外围函数中的return语句时,只有其中所有的延迟函数都执行完毕后,外围函数才会真正返回。
当外围函数中的代码引发运行恐慌时,只有其中所有的延迟函数都执行完毕后,该运行时恐慌才会真正被扩展至调用函数。

示例代码:

package main

import "fmt"

func main(){
    /*
    defer:词意"延迟,推迟"
        延迟执行函数。。等到其他的程序结束才能够执行。。

    如果多个函数被defer了,先延迟的后执行,后延迟的先执行。。

    如果一个被延迟的函数中涉及到了参数的传递:
        函数在defer的时候就已经传递了。只是延迟执行而已。
        o:=open(""...)
        defer o.close()
        o.exec()
        //..

     */
     defer printString("hello")
     fmt.Println("main中执行了。。。。")
     defer printString("world")

     a := 1
     defer fun1(a) //将a传递参数,只是暂时不执行。。
     a = 100
     fmt.Println(a) // 100
}
func fun1(num int){
    fmt.Println("我是fun1函数。。num:",num)
}

func printString(letter string){
    fmt.Println(letter)
}