溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

7.Go語言基礎(chǔ)之函數(shù)

發(fā)布時間:2020-06-11 00:10:10 來源:網(wǎng)絡(luò) 閱讀:322 作者:DevOperater 欄目:編程語言

1.函數(shù)

1.1函數(shù)定義

Go語言中定義函數(shù)使用fun關(guān)鍵字,具體格式為
func 函數(shù)名(參數(shù))(返回值){
    函數(shù)體
}

其中:
1.函數(shù)名:由字母,數(shù)字,下劃線組成。但函數(shù)名的第一個字母不能是數(shù)字。在同一個包內(nèi),函數(shù)名不能重名。
2.參數(shù):參數(shù)由變量名和變量類型組成。
3.返回值:可以只寫返回值類型,多個返回值必須用()包裹,并用,分隔。
4.函數(shù)體:實(shí)現(xiàn)指定功能的代碼塊。

定義一個求兩個數(shù)和的函數(shù):
func intSum(x int, y int) int {
    return x + y
}

函數(shù)的參數(shù)和返回值是可選的,我們也可以不傳參也沒有返回值。
func sayHello() {
    fmt.Println("Hello vita")
}

1.2函數(shù)的調(diào)用

定義了函數(shù)后,可以通過 函數(shù)名()的方式調(diào)用函數(shù)。
調(diào)用有返回值的函數(shù)時,可以不接收返回值。

package main

import "fmt"

func sayHello(){
    fmt.Println("hello vita")
}
func main() {
    sayHello()
}

結(jié)果:
hello vita

1.3參數(shù)

1.3.1類型簡寫

函數(shù)的參數(shù)中如果相鄰變量的類型相同,則可以省略類型。

package main

func intSum(x,y int)int{
    return x+y
}
func main() {
    intSum(1,2)
}

intSum函數(shù)有兩個參數(shù),x,y的類型都是int,因此可以省略x的類型,因?yàn)閥后面有類型說明。

1.3.2可變參數(shù)

可變參數(shù)即參數(shù)數(shù)量不固定。Go語言中的可變參數(shù)通過在參數(shù)后面加...來標(biāo)識。
注意:可變參數(shù)通常要作為函數(shù)的最后一個參數(shù)。

package main

import "fmt"

func intSum(x ...int)int{
    fmt.Println(x)//x是一個切片
    sum:=0
    for _,value :=range x{
        sum+=value
    }
    return sum
}
func main() {
    sum1 := intSum(1,2,3,4,5)
    sum2 := intSum(1,2,3,4,5,6,7)
    sum3 := intSum(1,2,3,4,5,6,7,8,9)
    fmt.Println(sum1)
    fmt.Println(sum2)
    fmt.Println(sum3)

}

結(jié)果:
[1 2 3 4 5]
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7 8 9]
15
28
45

1.3.3固定參數(shù)搭配可變參數(shù)

可變參數(shù)放在固定參數(shù)的后面

package main

import "fmt"

func intSum(x int,y ...int)int{
    sum:=x
    for _,value :=range y{
        sum+=value
    }
    return sum
}
func main() {
    sum1 := intSum(1,2,3,4,5)
    sum2 := intSum(1,2,3,4,5,6,7)
    sum3 := intSum(1,2,3,4,5,6,7,8,9)
    fmt.Println(sum1)
    fmt.Println(sum2)
    fmt.Println(sum3)

}

結(jié)果:
15
28
45

1.4返回值

1.4.1多返回值

Go語言中支持多返回值,函數(shù)如果有多個返回值,必須使用()把所有返回值包裹起來。

package main

func calc(x,y int)(int,int){
    sum :=x+y
    sub:=x-y
    return sum,sub
}
func main() {
    calc(3,2)

}

1.4.2返回值命名

函數(shù)定義時,可以給返回值命名,并在函數(shù)體中直接使用這些變量,最后通過return關(guān)鍵字返回。
package main

func calc(x,y int)(sum int,sub int){
    sum =x+y
    sub=x-y
    return 
}
func main() {
    calc(3,2)
}

2.函數(shù)進(jìn)階

2.1全局變量

全局變量是定義在函數(shù)外部的變量,他在程序整個運(yùn)行周期內(nèi)都有效。在函數(shù)中可以訪問到全局變量。

package main

import "fmt"

var num int64=100
func testGlobalVar(){
    fmt.Printf("num=%d\n",num)
}
func main() {
    testGlobalVar() //num=100
}

2.2局部變量

函數(shù)內(nèi)定義的變量,無法在函數(shù)外使用。
func testLocalVar() {
    //定義一個函數(shù)局部變量x,僅在該函數(shù)內(nèi)生效
    var x int64 = 100
    fmt.Printf("x=%d\n", x)
}

func main() {
    testLocalVar()
    fmt.Println(x) // 此時無法使用變量x
}
如果局部變量和全局變量重名,優(yōu)先訪問局部變量。

package main

import "fmt"

//定義全局變量num
var num int64 = 10

func testNum() {
    num := 100
    fmt.Printf("num=%d\n", num) // 函數(shù)中優(yōu)先使用局部變量
}
func main() {
    testNum() // num=100
}
語句塊中定義的變量,通常我們會在if條件判斷,for循環(huán),switch語句上使用這種定義變量的方式。

func testLocalVar2(x, y int) {
    fmt.Println(x, y) //函數(shù)的參數(shù)也是只在本函數(shù)中生效
    if x > 0 {
        z := 100 //變量z只在if語句塊生效
        fmt.Println(z)
    }
    //fmt.Println(z)//此處無法使用變量z
}
for循環(huán)語句中定義的變量,也只在for語句塊中生效
func testLocalVar3() {
    for i := 0; i < 10; i++ {
        fmt.Println(i) //變量i只在當(dāng)前for語句塊中生效
    }
    //fmt.Println(i) //此處無法使用變量i
}

2.3定義函數(shù)類型

我們可以使用type關(guān)鍵字定義一個函數(shù)類型,具體格式如下:
type calculation func(int, int) int

上面語句定義了一個calculation類型,它是一種函數(shù)類型,接收兩個int參數(shù),返回值類型為int。

簡單的說,凡是滿足這個條件的函數(shù)都是calculation類型的函數(shù),例如下面的add和sub是calculation類型。

func add(x, y int) int {
    return x + y
}

func sub(x, y int) int {
    return x - y
}

add和sub都能賦值給calculation類型的變量。
var c calculation
c = add

2.4函數(shù)類型變量

我們可以聲明函數(shù)類型的變量并且為該變量賦值:
func main() {
    var c calculation               // 聲明一個calculation類型的變量c
    c = add                         // 把a(bǔ)dd賦值給c
    fmt.Printf("type of c:%T\n", c) // type of c:main.calculation
    fmt.Println(c(1, 2))            // 像調(diào)用add一樣調(diào)用c

    f := add                        // 將函數(shù)add賦值給變量f1
    fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int
    fmt.Println(f(10, 20))          // 像調(diào)用add一樣調(diào)用f
}

3.高階函數(shù)

高階函數(shù)分為函數(shù)作為參數(shù)和函數(shù)作為返回值。

3.1函數(shù)作為參數(shù)

package main

import "fmt"

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

3.2函數(shù)作為返回值

package main

import (
    "errors"
    "fmt"
)

func add(x,y int) int  {
    return x+y
}
func sub(x,y int) int  {
    return x-y
}
func do(s string) (func(int, int) int,error)  {
    switch s {
    case "+":
        return add,nil
    case "-":
        return sub,nil
    default:
        err := errors.New("無法識別的操作符")
        return nil,err

    }
}
func main()  {

    addResult,err := do("+")
    fmt.Println(addResult(1,2),err)
    subResult,err := do("-")
    fmt.Println(subResult(2,1),err)
    informalResult,err := do(".")
    fmt.Println(informalResult,err)

}

結(jié)果:
3 <nil>
1 <nil>
<nil> 無法識別的操作符

Process finished with exit code 0

3.3匿名函數(shù)

函數(shù)可以作為返回值,但是在GO語言中,只能定義匿名函數(shù)。
匿名函數(shù)就是沒有函數(shù)名的函數(shù),匿名函數(shù)的定義格式如下:

func(參數(shù))(返回值){
    函數(shù)體
}

匿名函數(shù)因?yàn)闆]有函數(shù)名,所以不能像普通函數(shù)那樣調(diào)用,所以匿名函數(shù)需要保存到某個變量或者作為立即執(zhí)行函數(shù)。
匿名函數(shù)多用于實(shí)現(xiàn)回調(diào)函數(shù)和閉包。
package main

import "fmt"

func main()  {
    //將匿名函數(shù)保存到變量
    add := func(x,y int) {
        fmt.Println(x+y)
    }
    add(10,20) //通過變量調(diào)用匿名函數(shù)

    //自執(zhí)行函數(shù):匿名函數(shù)定義完加()直接執(zhí)行
    func(x,y int){
        fmt.Println(x+y)
    }(10,20)

}

結(jié)果:
30
30

Process finished with exit code 0

3.4閉包

閉包,在Python中,是外部函數(shù)的返回值是內(nèi)部函數(shù)的應(yīng)用,內(nèi)部函數(shù)使用了外部函數(shù)的變量,這就構(gòu)成了閉包。
在Go語言中,也是同樣的。
adder函數(shù)的返回值是一個內(nèi)部的匿名函數(shù),該匿名函數(shù)引用了外部的變量x,并且x變量在f的生命周期內(nèi)一直有效。
package main

import "fmt"

func adder() func(int) int  {
    var x int
    return func(y int) int {
        x += y
        return x
    }
}
func main()  {
    f := adder()
    fmt.Println(f(10))//10
    fmt.Println(f(20))//30
    fmt.Println(f(30))//60

    f1 := adder()
    fmt.Println(f1(40))//40
    fmt.Println(f1(50))//90
}

結(jié)果:
10
30
60
40
90

Process finished with exit code 0
閉包進(jìn)階示例1
package main

import "fmt"

func adder2(x int) func(int) int  {
    return func(y int) int {
        x += y
        return x
    }
}
func main()  {
    f := adder2(10)
    fmt.Println(f(10))//20
    fmt.Println(f(20))//40
    fmt.Println(f(30))//70

    f1 := adder2(20)
    fmt.Println(f1(40))//60
    fmt.Println(f1(50))//110
}

結(jié)果:
20
40
70
60
110

Process finished with exit code 0
閉包進(jìn)階示例2:
package main

import (
    "fmt"
    "strings"
)

func makeSuffixFunc(suffix string) func(string) string  {
    return func(name string) string {
        if !strings.HasSuffix(name,suffix){
            return name + suffix
        }
        return name
    }
}
func main()  {
    jpgFunc := makeSuffixFunc(".jpg")
    textFunc := makeSuffixFunc(".txt")
    fmt.Println(jpgFunc("test"))
    fmt.Println(textFunc("test"))
}

結(jié)果:
test.jpg
test.txt

Process finished with exit code 0
閉包進(jìn)階示例3:
package main

import "fmt"

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()  {
    f1,f2 := calc(10)
    fmt.Println(f1(1),f2(2))
    fmt.Println(f1(3),f2(4))
    fmt.Println(f1(5),f2(6))
}

結(jié)果:
11 9
12 8
13 7

Process finished with exit code 0

4.defer語句

4.1延遲調(diào)用特性

Go語言中的defer語句會將其后面跟隨的語句進(jìn)行延遲處理。
在defer歸屬的函數(shù)即將返回時,將延遲處理的語句按defer定義的逆序進(jìn)行執(zhí)行。
即先被defer的語句最后被執(zhí)行,最后被defer的語句,最先執(zhí)行。
package main

import "fmt"

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

結(jié)果:
start
end
3
2
1

Process finished with exit code 0
由于defer語句延遲調(diào)用的特性,所以defer語句能非常方便的處理資源釋放問題。比如:資源清理、文件關(guān)閉、解鎖及記錄時間等。

4.2defer執(zhí)行時機(jī)

在Go語言的函數(shù)中,return語句在底層并不是原子操作,它分為給返回值賦值和RET指令兩步。
defer語句執(zhí)行時機(jī)就在賦值操作后,RET指令執(zhí)行前。具體如圖:

7.Go語言基礎(chǔ)之函數(shù)

4.3defer經(jīng)典案例

package main

import "fmt"

// 關(guān)于defer的面試題
func f1() int {
    x := 5
    defer func() {
        x++
    }()
    return x // 1. 返回值=5  2. x++ 3. RET指令  ==> 5
}

func f2() (x int) {
    defer func() {
        x++
    }()
    return 5 // 1. (匯編)返回值=x(5) 2. x++  3.(匯編)RET ==> 6
}

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()
    return x // 1. (匯編)返回值=y(5)  2. x++  3.(匯編)RET  ==> 5
}
func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    return 5 //1. (匯編)返回值=x(5)  2. x++(函數(shù)內(nèi)部的x)  3.(匯編)RET  ==> 5
}
func main() {
    fmt.Println(f1())
    fmt.Println(f2())
    fmt.Println(f3())
    fmt.Println(f4())
}

4.4defer面試題

package main

import "fmt"

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    x := 1
    y := 2
    defer calc("AA", x, calc("A", x, y))
    x = 10
    defer calc("BB", x, calc("B", x, y))
    y = 20
}

提示:defer注冊要延遲執(zhí)行的函數(shù)時該函數(shù)所有的參數(shù)都需要確定其值
結(jié)果:
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4

Process finished with exit code 0

5.內(nèi)置函數(shù)

7.Go語言基礎(chǔ)之函數(shù)

5.1panic/recover

Go語言中,目前(Go1.12)是沒有異常機(jī)制的,但是使用panic/recover模式可以處理錯誤。
panic可以在任何地方引發(fā),但是recover只有在defer調(diào)用的函數(shù)中有效。
package main

import "fmt"

func funcA()  {
    fmt.Println("func A")
}
func funcB()  {
    panic("func B")
}
func funcC()  {
    fmt.Println("func C")
}

func main() {
    funcA()
    funcB()
    funcC()
}

7.Go語言基礎(chǔ)之函數(shù)

在程序運(yùn)行期間,funcB中引發(fā)了panic,導(dǎo)致程序崩潰,異常退出了。
這個時候,我們可以通過recover將程序恢復(fù)回來,繼續(xù)往后執(zhí)行。
package main

import "fmt"

func funcA()  {
    fmt.Println("func A")
}
func funcB()  {
    defer func() {
        err := recover()
        if err != nil{
            fmt.Println("recover in B")
        }
    }()
    panic("func B")
}
func funcC()  {
    fmt.Println("func C")
}

func main() {
    funcA()
    funcB()
    funcC()
}

結(jié)果:
func A
recover in B
func C

Process finished with exit code 0
注意:
1.recover()必須搭配defer使用。
2.2.defer一定要在可能引發(fā)panic的語句之前定義。

6.練習(xí)題

/*
你有50枚金幣,需要分配給以下幾個人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配規(guī)則如下:
a. 名字中每包含1個'e'或'E'分1枚金幣
b. 名字中每包含1個'i'或'I'分2枚金幣
c. 名字中每包含1個'o'或'O'分3枚金幣
d: 名字中每包含1個'u'或'U'分4枚金幣
寫一個程序,計算每個用戶分到多少金幣,以及最后剩余多少金幣?
程序結(jié)構(gòu)如下,請實(shí)現(xiàn) ‘dispatchCoin’ 函數(shù)
*/
var (
    coins = 50
    users = []string{
        "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
    }
    distribution = make(map[string]int, len(users))
)

func main() {
    left := dispatchCoin()
    fmt.Println("剩下:", left)
}
package main

import (
    "fmt"
    "strings"
)

var (
    coins = 50
    users = []string{
        "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
    }
    distribution = make(map[string]int, len(users))
)

func dispatchCoin()  int{
    count := 0
    for _,value := range users{

        eCount := strings.Count(strings.ToLower(value),"e")
        iCount := strings.Count(strings.ToLower(value),"i")
        oCount := strings.Count(strings.ToLower(value),"o")
        uCount := strings.Count(strings.ToLower(value),"u")
        personCount := eCount*1+iCount*2+oCount*3+uCount*4
        fmt.Printf("Pserson:%s,coins:%d\n",value,personCount)
        count += personCount

    }
    return coins-count

}
func main() {
    left := dispatchCoin()
    fmt.Println("剩下:", left)
}

結(jié)果:
Pserson:Matthew,coins:1
Pserson:Sarah,coins:0
Pserson:Augustus,coins:12
Pserson:Heidi,coins:5
Pserson:Emilie,coins:6
Pserson:Peter,coins:2
Pserson:Giana,coins:2
Pserson:Adriano,coins:5
Pserson:Aaron,coins:3
Pserson:Elizabeth,coins:4
剩下: 10

Process finished with exit code 0
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI