欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言中的流程控制結構和函數(shù)詳解

 更新時間:2014年10月28日 11:53:05   投稿:junjie  
這篇文章主要介紹了Go語言中的流程控制結構和函數(shù)詳解,本文詳細講解了if、goto、for、switch等控制語句,同時對函數(shù)相關知識做了講解,需要的朋友可以參考下

這小節(jié)我們要介紹Go里面的流程控制以及函數(shù)操作。

流程控制

流程控制在編程語言中是最偉大的發(fā)明了,因為有了它,你可以通過很簡單的流程描述來表達很復雜的邏輯。Go中流程控制分三大類:條件判斷,循環(huán)控制和無條件跳轉。

if

if也許是各種編程語言中最常見的了,它的語法概括起來就是:如果滿足條件就做某事,否則做另一件事。

Go里面if條件判斷語句中不需要括號,如下代碼所示:

復制代碼 代碼如下:

if x > 10 {
    fmt.Println("x is greater than 10")
} else {
    fmt.Println("x is less than 10")
}

Go的if還有一個強大的地方就是條件判斷語句里面允許聲明一個變量,這個變量的作用域只能在該條件邏輯塊內,其他地方就不起作用了,如下所示:

復制代碼 代碼如下:

// 計算獲取值x,然后根據(jù)x返回的大小,判斷是否大于10。
if x := computedValue(); x > 10 {
    fmt.Println("x is greater than 10")
} else {
    fmt.Println("x is less than 10")
}

//這個地方如果這樣調用就編譯出錯了,因為x是條件里面的變量
fmt.Println(x)

多個條件的時候如下所示:

復制代碼 代碼如下:

if integer == 3 {
    fmt.Println("The integer is equal to 3")
} else if integer < 3 {
    fmt.Println("The integer is less than 3")
} else {
    fmt.Println("The integer is greater than 3")
}

goto

Go有goto語句——請明智地使用它。用goto跳轉到必須在當前函數(shù)內定義的標簽。例如假設這樣一個循環(huán):

復制代碼 代碼如下:

func myFunc() {
    i := 0
Here:   //這行的第一個詞,以冒號結束作為標簽
    println(i)
    i++
    goto Here   //跳轉到Here去
}

標簽名是大小寫敏感的。

for

Go里面最強大的一個控制邏輯就是for,它即可以用來循環(huán)讀取數(shù)據(jù),又可以當作while來控制邏輯,還能迭代操作。它的語法如下:

復制代碼 代碼如下:

for expression1; expression2; expression3 {
    //...
}

expression1、expression2和expression3都是表達式,其中expression1和expression3是變量聲明或者函數(shù)調用返回值之類的,expression2是用來條件判斷,expression1在循環(huán)開始之前調用,expression3在每輪循環(huán)結束之時調用。

一個例子比上面講那么多更有用,那么我們看看下面的例子吧:

復制代碼 代碼如下:

package main
import "fmt"

func main(){
    sum := 0;
    for index:=0; index < 10 ; index++ {
        sum += index
    }
    fmt.Println("sum is equal to ", sum)
}
// 輸出:sum is equal to 45

有些時候需要進行多個賦值操作,由于Go里面沒有,操作符,那么可以使用平行賦值i, j = i+1, j-1

有些時候如果我們忽略expression1和expression3:

復制代碼 代碼如下:

sum := 1
for ; sum < 1000;  {
    sum += sum
}

其中;也可以省略,那么就變成如下的代碼了,是不是似曾相識?對,這就是while的功能。
復制代碼 代碼如下:

sum := 1
for sum < 1000 {
    sum += sum
}

在循環(huán)里面有兩個關鍵操作break和continue ,break操作是跳出當前循環(huán),continue是跳過本次循環(huán)。當嵌套過深的時候,break可以配合標簽使用,即跳轉至標簽所指定的位置,詳細參考如下例子:

復制代碼 代碼如下:

for index := 10; index>0; index-- {
    if index == 5{
        break // 或者continue
    }
    fmt.Println(index)
}
// break打印出來10、9、8、7、6
// continue打印出來10、9、8、7、6、4、3、2、1

break和continue還可以跟著標號,用來跳到多重循環(huán)中的外層循環(huán)

for配合range可以用于讀取slice和map的數(shù)據(jù):

復制代碼 代碼如下:

for k,v:=range map {
    fmt.Println("map's key:",k)
    fmt.Println("map's val:",v)
}

switch

有些時候你需要寫很多的if-else來實現(xiàn)一些邏輯處理,這個時候代碼看上去就很丑很冗長,而且也不易于以后的維護,這個時候switch就能很好的解決這個問題。它的語法如下

復制代碼 代碼如下:

switch sExpr {
case expr1:
    some instructions
case expr2:
    some other instructions
case expr3:
    some other instructions
default:
    other code
}

sExpr和expr1、expr2、expr3的類型必須一致。Go的switch非常靈活,表達式不必是常量或整數(shù),執(zhí)行的過程從上至下,直到找到匹配項;而如果switch沒有表達式,它會匹配true。

復制代碼 代碼如下:

i := 10
switch i {
case 1:
    fmt.Println("i is equal to 1")
case 2, 3, 4:
    fmt.Println("i is equal to 2, 3 or 4")
case 10:
    fmt.Println("i is equal to 10")
default:
    fmt.Println("All I know is that i is an integer")
}

在第5行中,我們把很多值聚合在了一個case里面,同時,Go里面switch默認相當于每個case最后帶有break,匹配成功后不會自動向下執(zhí)行其他case,而是跳出整個switch, 但是可以使用fallthrough強制執(zhí)行后面的case代碼。

復制代碼 代碼如下:

integer := 6
switch integer {
    case 4:
    fmt.Println("The integer was <= 4")
    fallthrough
    case 5:
    fmt.Println("The integer was <= 5")
    fallthrough
    case 6:
    fmt.Println("The integer was <= 6")
    fallthrough
    case 7:
    fmt.Println("The integer was <= 7")
    fallthrough
    case 8:
    fmt.Println("The integer was <= 8")
    fallthrough
    default:
    fmt.Println("default case")
}

上面的程序將輸出
復制代碼 代碼如下:

The integer was <= 6
The integer was <= 7
The integer was <= 8
default case

函數(shù)

函數(shù)是Go里面的核心設計,它通過關鍵字func來聲明,它的格式如下:

復制代碼 代碼如下:

func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
    //這里是處理邏輯代碼
    //返回多個值
    return value1, value2
}

上面的代碼我們看出

1.關鍵字func用來聲明一個函數(shù)funcName
2.函數(shù)可以有一個或者多個參數(shù),每個參數(shù)后面帶有類型,通過,分隔
3.函數(shù)可以返回多個值
4.上面返回值聲明了兩個變量output1和output2,如果你不想聲明也可以,直接就兩個類型
5.如果只有一個返回值且不聲明返回值變量,那么你可以省略 包括返回值 的括號
6.如果沒有返回值,那么就直接省略最后的返回信息
7.如果有返回值, 那么必須在函數(shù)的外層添加return語句

下面我們來看一個實際應用函數(shù)的例子(用來計算Max值)

復制代碼 代碼如下:

package main
import "fmt"

// 返回a、b中最大值.
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func main() {
    x := 3
    y := 4
    z := 5

    max_xy := max(x, y) //調用函數(shù)max(x, y)
    max_xz := max(x, z) //調用函數(shù)max(x, z)

    fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
    fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
    fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在這直接調用它
}


上面這個里面我們可以看到max函數(shù)有兩個參數(shù),它們的類型都是int,那么第一個變量的類型可以省略(即 a,b int,而非 a int, b int),默認為離它最近的類型,同理多于2個同類型的變量或者返回值。同時我們注意到它的返回值就是一個類型,這個就是省略寫法。

多個返回值

Go語言比C更先進的特性,其中一點就是函數(shù)能夠返回多個值。

我們直接上代碼看例子:

復制代碼 代碼如下:

package main
import "fmt"

//返回 A+B 和 A*B
func SumAndProduct(A, B int) (int, int) {
    return A+B, A*B
}

func main() {
    x := 3
    y := 4

    xPLUSy, xTIMESy := SumAndProduct(x, y)

    fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
    fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}

上面的例子我們可以看到直接返回了兩個參數(shù),當然我們也可以命名返回參數(shù)的變量,這個例子里面只是用了兩個類型,我們也可以改成如下這樣的定義,然后返回的時候不用帶上變量名,因為直接在函數(shù)里面初始化了。但如果你的函數(shù)是導出的(首字母大寫),官方建議:最好命名返回值,因為不命名返回值,雖然使得代碼更加簡潔了,但是會造成生成的文檔可讀性差。

復制代碼 代碼如下:

func SumAndProduct(A, B int) (add int, Multiplied int) {
    add = A+B
    Multiplied = A*B
    return
}

變參

Go函數(shù)支持變參。接受變參的函數(shù)是有著不定數(shù)量的參數(shù)的。為了做到這點,首先需要定義函數(shù)使其接受變參:

復制代碼 代碼如下:

func myfunc(arg ...int) {}

arg ...int告訴Go這個函數(shù)接受不定數(shù)量的參數(shù)。注意,這些參數(shù)的類型全部是int。在函數(shù)體中,變量arg是一個int的slice:
復制代碼 代碼如下:

for _, n := range arg {
    fmt.Printf("And the number is: %d\n", n)
}

傳值與傳指針

當我們傳一個參數(shù)值到被調用函數(shù)里面時,實際上是傳了這個值的一份copy,當在被調用函數(shù)中修改參數(shù)值的時候,調用函數(shù)中相應實參不會發(fā)生任何變化,因為數(shù)值變化只作用在copy上。

為了驗證我們上面的說法,我們來看一個例子

復制代碼 代碼如下:

package main
import "fmt"

//簡單的一個函數(shù),實現(xiàn)了參數(shù)+1的操作
func add1(a int) int {
    a = a+1 // 我們改變了a的值
    return a //返回一個新值
}

func main() {
    x := 3

    fmt.Println("x = ", x)  // 應該輸出 "x = 3"

    x1 := add1(x)  //調用add1(x)

    fmt.Println("x+1 = ", x1) // 應該輸出"x+1 = 4"
    fmt.Println("x = ", x)    // 應該輸出"x = 3"
}

看到了嗎?雖然我們調用了add1函數(shù),并且在add1中執(zhí)行a = a+1操作,但是上面例子中x變量的值沒有發(fā)生變化

理由很簡單:因為當我們調用add1的時候,add1接收的參數(shù)其實是x的copy,而不是x本身。

那你也許會問了,如果真的需要傳這個x本身,該怎么辦呢?

這就牽扯到了所謂的指針。我們知道,變量在內存中是存放于一定地址上的,修改變量實際是修改變量地址處的內存。只有add1函數(shù)知道x變量所在的地址,才能修改x變量的值。所以我們需要將x所在地址&x傳入函數(shù),并將函數(shù)的參數(shù)的類型由int改為*int,即改為指針類型,才能在函數(shù)中修改x變量的值。此時參數(shù)仍然是按copy傳遞的,只是copy的是一個指針。請看下面的例子:

復制代碼 代碼如下:

package main
import "fmt"

//簡單的一個函數(shù),實現(xiàn)了參數(shù)+1的操作
func add1(a *int) int { // 請注意,
    *a = *a+1 // 修改了a的值
    return *a // 返回新值
}

func main() {
    x := 3

    fmt.Println("x = ", x)  // 應該輸出 "x = 3"

    x1 := add1(&x)  // 調用 add1(&x) 傳x的地址

    fmt.Println("x+1 = ", x1) // 應該輸出 "x+1 = 4"
    fmt.Println("x = ", x)    // 應該輸出 "x = 4"
}

這樣,我們就達到了修改x的目的。那么到底傳指針有什么好處呢?

1.傳指針使得多個函數(shù)能操作同一個對象。
2.傳指針比較輕量級 (8bytes),只是傳內存地址,我們可以用指針傳遞體積大的結構體。如果用參數(shù)值傳遞的話, 在每次copy上面就會花費相對較多的系統(tǒng)開銷(內存和時間)。所以當你要傳遞大的結構體的時候,用指針是一個明智的選擇。
3.Go語言中string,slice,map這三種類型的實現(xiàn)機制類似指針,所以可以直接傳遞,而不用取地址后傳遞指針。(注:若函數(shù)需改變slice的長度,則仍需要取地址傳遞指針)

defer

Go語言中有種不錯的設計,即延遲(defer)語句,你可以在函數(shù)中添加多個defer語句。當函數(shù)執(zhí)行到最后時,這些defer語句會按照逆序執(zhí)行,最后該函數(shù)返回。特別是當你在進行一些打開資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源泄露等問題。如下代碼所示,我們一般寫打開一個資源是這樣操作的:

復制代碼 代碼如下:

func ReadWrite() bool {
    file.Open("file")
// 做一些工作
    if failureX {
        file.Close()
        return false
    }

    if failureY {
        file.Close()
        return false
    }

    file.Close()
    return true
}

我們看到上面有很多重復的代碼,Go的defer有效解決了這個問題。使用它后,不但代碼量減少了很多,而且程序變得更優(yōu)雅。在defer后指定的函數(shù)會在函數(shù)退出前調用。

復制代碼 代碼如下:

func ReadWrite() bool {
    file.Open("file")
    defer file.Close()
    if failureX {
        return false
    }
    if failureY {
        return false
    }
    return true
}

如果有很多調用defer,那么defer是采用后進先出模式,所以如下代碼會輸出4 3 2 1 0
復制代碼 代碼如下:

for i := 0; i < 5; i++ {
    defer fmt.Printf("%d ", i)
}

函數(shù)作為值、類型

在Go中函數(shù)也是一種變量,我們可以通過type來定義它,它的類型就是所有擁有相同的參數(shù),相同的返回值的一種類型

復制代碼 代碼如下:

type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])

函數(shù)作為類型到底有什么好處呢?那就是可以把這個類型的函數(shù)當做值來傳遞,請看下面的例子
復制代碼 代碼如下:

package main
import "fmt"

type testInt func(int) bool // 聲明了一個函數(shù)類型

func isOdd(integer int) bool {
    if integer%2 == 0 {
        return false
    }
    return true
}

func isEven(integer int) bool {
    if integer%2 == 0 {
        return true
    }
    return false
}

// 聲明的函數(shù)類型在這個地方當做了一個參數(shù)

func filter(slice []int, f testInt) []int {
    var result []int
    for _, value := range slice {
        if f(value) {
            result = append(result, value)
        }
    }
    return result
}

func main(){
    slice := []int {1, 2, 3, 4, 5, 7}
    fmt.Println("slice = ", slice)
    odd := filter(slice, isOdd)    // 函數(shù)當做值來傳遞了
    fmt.Println("Odd elements of slice are: ", odd)
    even := filter(slice, isEven)  // 函數(shù)當做值來傳遞了
    fmt.Println("Even elements of slice are: ", even)
}


函數(shù)當做值和類型在我們寫一些通用接口的時候非常有用,通過上面例子我們看到testInt這個類型是一個函數(shù)類型,然后兩個filter函數(shù)的參數(shù)和返回值與testInt類型是一樣的,但是我們可以實現(xiàn)很多種的邏輯,這樣使得我們的程序變得非常的靈活。

Panic和Recover

Go沒有像Java那樣的異常機制,它不能拋出異常,而是使用了panic和recover機制。一定要記住,你應當把它作為最后的手段來使用,也就是說,你的代碼中應當沒有,或者很少有panic的東西。這是個強大的工具,請明智地使用它。那么,我們應該如何使用它呢?

Panic

是一個內建函數(shù),可以中斷原有的控制流程,進入一個令人恐慌的流程中。當函數(shù)F調用panic,函數(shù)F的執(zhí)行被中斷,但是F中的延遲函數(shù)會正常執(zhí)行,然后F返回到調用它的地方。在調用的地方,F(xiàn)的行為就像調用了panic。這一過程繼續(xù)向上,直到發(fā)生panic的goroutine中所有調用的函數(shù)返回,此時程序退出??只趴梢灾苯诱{用panic產生。也可以由運行時錯誤產生,例如訪問越界的數(shù)組。

Recover

是一個內建的函數(shù),可以讓進入令人恐慌的流程中的goroutine恢復過來。recover僅在延遲函數(shù)中有效。在正常的執(zhí)行過程中,調用recover會返回nil,并且沒有其它任何效果。如果當前的goroutine陷入恐慌,調用recover可以捕獲到panic的輸入值,并且恢復正常的執(zhí)行。

下面這個函數(shù)演示了如何在過程中使用panic

復制代碼 代碼如下:

var user = os.Getenv("USER")

func init() {
    if user == "" {
        panic("no value for $USER")
    }
}

下面這個函數(shù)檢查作為其參數(shù)的函數(shù)在執(zhí)行時是否會產生panic:

復制代碼 代碼如下:

func throwsPanic(f func()) (b bool) {
    defer func() {
        if x := recover(); x != nil {
            b = true
        }
    }()
    f() //執(zhí)行函數(shù)f,如果f中出現(xiàn)了panic,那么就可以恢復回來
    return
}

main函數(shù)和init函數(shù)

Go里面有兩個保留的函數(shù):init函數(shù)(能夠應用于所有的package)和main函數(shù)(只能應用于package main)。這兩個函數(shù)在定義時不能有任何的參數(shù)和返回值。雖然一個package里面可以寫任意多個init函數(shù),但這無論是對于可讀性還是以后的可維護性來說,我們都強烈建議用戶在一個package中每個文件只寫一個init函數(shù)。

Go程序會自動調用init()和main(),所以你不需要在任何地方調用這兩個函數(shù)。每個package中的init函數(shù)都是可選的,但package main就必須包含一個main函數(shù)。

程序的初始化和執(zhí)行都起始于main包。如果main包還導入了其它的包,那么就會在編譯時將它們依次導入。有時一個包會被多個包同時導入,那么它只會被導入一次(例如很多包可能都會用到fmt包,但它只會被導入一次,因為沒有必要導入多次)。當一個包被導入時,如果該包還導入了其它的包,那么會先將其它包導入進來,然后再對這些包中的包級常量和變量進行初始化,接著執(zhí)行init函數(shù)(如果有的話),依次類推。等所有被導入的包都加載完畢了,就會開始對main包中的包級常量和變量進行初始化,然后執(zhí)行main包中的init函數(shù)(如果存在的話),最后執(zhí)行main函數(shù)。下圖詳細地解釋了整個執(zhí)行過程:

圖2.6 main函數(shù)引入包初始化流程圖

import

我們在寫Go代碼的時候經(jīng)常用到import這個命令用來導入包文件,而我們經(jīng)??吹降姆绞絽⒖既缦拢?br />

復制代碼 代碼如下:

import(
    "fmt"
)

然后我們代碼里面可以通過如下的方式調用
復制代碼 代碼如下:

fmt.Println("hello world")

上面這個fmt是Go語言的標準庫,其實是去GOROOT環(huán)境變量指定目錄下去加載該模塊,當然Go的import還支持如下兩種方式來加載自己寫的模塊:

1.相對路徑

復制代碼 代碼如下:

import “./model” //當前文件同一目錄的model目錄,但是不建議這種方式來import

2.絕對路徑
復制代碼 代碼如下:

import “shorturl/model” //加載gopath/src/shorturl/model模塊

上面展示了一些import常用的幾種方式,但是還有一些特殊的import,讓很多新手很費解,下面我們來一一講解一下到底是怎么一回事

點操作

我們有時候會看到如下的方式導入包

復制代碼 代碼如下:

import(
    . "fmt"
)

這個點操作的含義就是這個包導入之后在你調用這個包的函數(shù)時,你可以省略前綴的包名,也就是前面你調用的fmt.Println("hello world")可以省略的寫成Println("hello world")

別名操作

別名操作顧名思義我們可以把包命名成另一個我們用起來容易記憶的名字

復制代碼 代碼如下:

import(
    f "fmt"
)

別名操作的話調用包函數(shù)時前綴變成了我們的前綴,即f.Println("hello world")。

_操作

這個操作經(jīng)常是讓很多人費解的一個操作符,請看下面這個import

復制代碼 代碼如下:

import (
    "database/sql"
    _ "github.com/ziutek/mymysql/godrv"
)

_操作其實是引入該包,而不直接使用包里面的函數(shù),而是調用了該包里面的init函數(shù)。

相關文章

  • 提升編程技能:學習如何在Go語言中正確格式化時間

    提升編程技能:學習如何在Go語言中正確格式化時間

    想知道如何在Go語言中輕松地格式化時間嗎?別再浪費時間了!本文將帶你快速入門,讓你的代碼更加優(yōu)雅高效,快來學習吧!
    2024-01-01
  • GO 語言學習指南

    GO 語言學習指南

    這篇文章主要介紹了GO 語言學習指南,介紹的相當全面,有需要的小伙伴參考下吧。
    2015-01-01
  • Go語言中數(shù)組的基本用法演示

    Go語言中數(shù)組的基本用法演示

    這篇文章主要介紹了Go語言中數(shù)組的基本用法演示,包括一個冒泡排序算法的簡單實現(xiàn),需要的朋友可以參考下
    2015-10-10
  • 詳解Go語言中配置文件使用與日志配置

    詳解Go語言中配置文件使用與日志配置

    這篇文章主要為大家詳細講解一下Go語言中調整項目目錄結構、增加配置文件使用和增加日志配置的方法,文中示例代碼講解詳細,需要的可以參考一下
    2022-06-06
  • 在go語言中安裝與使用protobuf的方法詳解

    在go語言中安裝與使用protobuf的方法詳解

    protobuf以前只支持C++, Python和Java等語言, Go語言出來后, 作為親兒子, 那有不支持的道理呢? 這篇文章主要給大家介紹了關于在go語言中使用protobuf的相關資料,文中介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • Golang中的自定義函數(shù)詳解

    Golang中的自定義函數(shù)詳解

    函數(shù)構成代碼執(zhí)行的邏輯結構。在Go語言中,函數(shù)的基本組成為:關鍵字func、函數(shù)名、參數(shù)列表、返回值、函數(shù)體和返回語句。
    2018-10-10
  • golang?gorm開發(fā)架構及寫插件示例

    golang?gorm開發(fā)架構及寫插件示例

    這篇文章主要為大家介紹了golang?gorm開發(fā)架構及寫插件的詳細示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • 在 Golang 中實現(xiàn)一個簡單的Http中間件過程詳解

    在 Golang 中實現(xiàn)一個簡單的Http中間件過程詳解

    本文在go web中簡單的實現(xiàn)了中間件的機制,這樣帶來的好處也是顯而易見的,當然社區(qū)也有一些成熟的 middleware 組件,包括 Gin 一些Web框架中也包含了 middleware 相關的功能,具體內容詳情跟隨小編一起看看吧
    2021-07-07
  • Go語言對字符串進行MD5加密的方法

    Go語言對字符串進行MD5加密的方法

    這篇文章主要介紹了Go語言對字符串進行MD5加密的方法,實例分析了Go語言對字符串進行md5加密的技巧,需要的朋友可以參考下
    2015-03-03
  • Mac GoLand打不開(閃退)也不報錯的解決方案

    Mac GoLand打不開(閃退)也不報錯的解決方案

    這篇文章主要介紹了Mac GoLand打不開(閃退)也不報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04

最新評論