【跟着咱們學Golang】流程控制

做爲一門高級語言,Go一樣提供了流程控制的支持。在瞭解了基礎結構以後,繼續學習Go的流程控制,裏面涉及到的基礎結構的內容還能對其有更多的瞭解。git

說流程控制以前先說一下interface,由於後續在流程控制中會穿插着對interface的使用。github

interface

interface是一切類型的基類型,相似於Java中的基類Obejct,全部的結構都是interface的實現,由於interface基類型沒有定義任何的函數,因此其餘任何結構都認爲是interface的實現。固然,也能夠本身定義interface本身去實現相應的函數,這個下期面向對象的時候會詳細解釋。這裏先簡單說明interface做爲基類型時的使用。微信

在Java中,全部的類型都是Object的子類,因此聲明對象時能夠將對象的類型聲明爲Object,在賦值時給一個子類型,在Go中一樣能夠,但僅限於針對interface聲明的使用(仍是會牽涉到面向對象的東西),也就是說,聲明時能夠將變量聲明爲interface類型,賦值時給一個其餘基礎類型的值,這是最簡單的interface做爲基類型的使用。app

var hello interface{} = "hello world"

fmt.Println(hello)

例子中聲明hello時,聲明的類型是interface{}類型,並非string類型,可是賦值時給的是string類型,說明hello實際類型仍是string類型。具體的類型轉換下面會詳細說明。less

if-else

Go中的if-else結構的用戶與Java中的特別的相似,僅僅區別在二者的語法上面,Go的語法爲:函數

if 條件1 {
    ...
} else if 條件2 && 條件3 {
    ...
} else {
    ...
}

Go對語法的要求沒有Java那麼嚴格,對於括號能夠帶,也能夠不帶。一樣的,Go也支持&&||!這樣的運算符進行多個條件的關聯判斷學習

func max(a, b int) (max int) {
    if a > b {
        max = a
    } else if a == b {
        max = a
    } else {
        max = b
    }
    
    return 
}

斷言

斷言在Go中是一種類型轉換的語法,可否方便的進行類型的轉換。Go語言中簡單的斷言語法爲 value := element.(type)設計

//value := element.(type) //type爲要轉換的類型

var hello interface{} = "helloworld"

fmt.Println(hello.(string))
fmt.Println(hello.(int))//該行會報錯,由於hello實際類型是string類型

稍微不注意,直接轉換的話就會出現異常,因此通常不推薦使用簡單的語法,而是用高級語法 value, ok := element.(type),這也是在if-else結構中講解的緣由。code

// value, ok := element.(type) //type爲要轉換的類型,ok爲是否成功轉換,類型爲bool,value爲實際轉換的值

var hello interface{} = "helloworld"

helloS, ok := hello.(string)
if ok {
    fmt.Println("hello tranfer successfully : ", helloS)
} else {
    fmt.Println("hello transfer failed")
}

使用高級語法能保證在運行的時候不會出現錯誤,保證程序的持續執行,這是比較推薦的作法。orm

map斷言是map的一種高級用法。

//map的斷言
// value, ok := m[key] //這裏的OK再也不是簡單的成功或者失敗,理解成是否存在更合適
var m = make(map[string]interface{})//建立map的方式,具體make的用法後續會講解

m["key1"] = "value1"
value1, ok := m["key1"]
if ok {
	fmt.Println("map m contain 'key1' ", value1)
} else {
	fmt.Println("map m contain 'key1'")
}

map在斷言的使用上好像是天生支持似的,不須要進行Contains函數的校驗等,直接使用,平時在代碼中使用的也是很是多。簡直不要太好用。

switch

switch感受像是if-else的高級版,一樣是進行條件判斷的結構,不一樣的條件執行不一樣的語句。語法相似Java,Java中只能使用byte、int、short、char和string,在Go中可沒有這些限制。 從上至下的判斷,直到找到匹配的case或者執行default語句,case結尾也不須要break進行跳出流程操做,執行完自動跳出。相反,若是想執行下一個case的話,須要使用fallthrough關鍵字進行下沉操做, 這時候下一條case的條件將被忽略。

switch value1 { //大括號必須與switch保持一行
    case value1:
        ...
    case value2, value3://多個條件使用逗號隔開
        ...
    default://沒有符合的條件執行默認
        ...
}

語法規定 switch後跟的value1能夠是任意類型(甚至是不寫),可是case後的條件必須和switch後的value保持相同類型

grade := 10
switch grade {
//case code < 60://code爲int類型,不能使用code < 60做爲case條件
case 10:
	fmt.Println("不及格")
case 70:
	fmt.Println("及格")
default:
	fmt.Println("無效的分數")
}

//用於類型斷言
switch hello.(type) {
case string:
	fmt.Println("hello is string")
case int:
	fmt.Println("hello is int")
default:
	fmt.Println("hello is unknown type")
}

switch {//直接判斷case
case a < b:
	fmt.Println("a less than b")
	fallthrough //緊接着執行下一個case,不須要進行判斷
case a > b:
	fmt.Println("a bigger than b")
}

for

說到循環、重複執行等首先想到的就是for,Go一樣提供了支持,相對於Java,Go中for的使用更靈活。 一樣的,想跳出for循環時使用break關鍵字。

//語法一
for init;條件;賦值{//左側大括號必須與for同行
    ...
}

//語法二
for 條件 {//左側大括號必須與for同行
    ...
}

//語法三
//這是個死循環
for {//左側大括號必須與for同行
    ...
}

//語法四
for index, value := range slice/array/map {//range是關鍵字
    ...
}

上手就是一個排序來介紹最基本的for結構

a := []int{1, 3, 9, 4, 1, 4, 6, 132, 1, 29, 43, 55, 89, 46}
for i := 0; i < len(a); i++ {//len爲Go內置函數
	for j := i + 1; j < len(a); j++ {
		if a[i] > a[j] {
			a[i], a[j] = a[j], a[i]
		}
	}
}

fmt.Println(a)//結果:[1 1 1 3 4 4 6 9 29 43 46 55 89 132]

只寫條件的for循環,相似Java中的while

var i = 0
for i < len(a) {
	fmt.Print(a[i],"  ")
	i++
}//結果: 1  1  1  3  4  4  6  9  29  43  46  55  89  132

死循環寫法更簡單了,不過須要注意使用break進行跳出,不然電腦就該嗡嗡嗡~響不停了

i = 0
for{
	if i < len(a) {
		fmt.Print(a[i], " ")
		i++
	} else {
		break
	}
}//結果: 1 1 1 3 4 4 6 9 29 43 46 55 89 132

最牛的語法四就是爲slice和array使用的,能遍歷全部的集合。當遍歷slice和array時,index指的是其中的索引位置;遍歷map時指的就是key了。請看下面的例子

for index, value := range a {
	fmt.Printf("index: %d, value: %d \n", index, value)
}
/*
結果:
index: 0, value: 1
index: 1, value: 1
index: 2, value: 1
index: 3, value: 3
index: 4, value: 4
index: 5, value: 4
index: 6, value: 6
index: 7, value: 9
index: 8, value: 29
index: 9, value: 43
index: 10, value: 46
index: 11, value: 55
index: 12, value: 89
index: 13, value: 132
 */
m := map[string]string{}
m["hello"] = "world"
m["hey"] = "bye"

for key, value := range m {
	fmt.Printf("key: %s, value: %s \n", key, value)
}
/*
結果:
key: hello, value: world
key: hey, value: bye
 */

select

select 第一眼看到可能會想到SQL中的選擇,可是它也是Go中的一個流程控制關鍵字。

select的使用主要是結合channel來使用,因此這裏要是講解channels會設計到不少東西,咱們後期會作詳細的講解,這裏先作select的介紹。

select的語法跟switch相似,用於選擇合適的條件進行執行相應的邏輯,但牽涉到channel,因此select中的case都是對channel的操做,只能是往channel中讀或者寫。

select {
    case channel讀操做:
        ...
    case channel寫操做:
        ...
    default:
        ...
}

注意點:

channel包含讀和寫兩種操做,case中必須包含一種操做

case的執行是無序的、隨機的,select會執行任意一個可執行的case

沒有可執行的case時會執行default,沒有default的話就會阻塞,等待可執行的channel

下面是一個簡單的例子實現,先不要深究內容含義,瞭解select語法便可

c := make(chan int, 1)
select {
case c <- 1:
	fmt.Println("push into channel")
case <-c:
	fmt.Println("get from channel")
default:
	fmt.Println("default")
}
//結果:push into channel

...

不要懷疑標題,標題就是三個英文點,這裏要說一下這三個點的問題,以此來解釋一下爲何在使用fmt.Println()和fmt.Printf()函數時使用逗號將參數隔開的問題。

咱們先看一下fmt.Println()和fmt.Printf()的源碼

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func Printf(format string, a ...interface{}) (n int, err error) {
	return Fprintf(os.Stdout, format, a...)
}

這裏看到Println()和Printf()這兩個函數其實就一個入參,爲何我能用逗號分隔從而給多個參數呢?

緣由是這樣的,a ...interface{}這個實際上是slice的一個特殊用法,說明這定義的是一個可變參數,能夠接收不定數量的統一類型的參數,定義爲...interfaec{}就能夠接收不定數量的任意基礎類型。定義可變參數時的語法就是在類型前面加上這三個點,這裏使用interface就說明能夠接收任何類型

想使用這可變參數的語法也很簡單,能夠將其做爲slice使用,也能夠繼續將其做爲可變參數使用。使用可變參數的語法就是在定義的後面加上這三個點。下面看例子

func main(){
    definedThreeDot("jack", "rose", "tom", "jerry")//定義多個參數來使用可變參數
}
func definedThreeDot(source ...string) {//定義可變參數,定義時在類型前面加上三個點
	useThreeDot(source...)//將可變參數做爲可變參數使用,使用時在定義後面加上三個點
	useThreeDotAsSlice(source)//將可變參數做爲slice使用
}

func useThreeDotAsSlice(ss []string) {//定義slice來接收可變參數
	fmt.Println(ss)//直接打印slice
}

func useThreeDot(ss ...string) {//定義可變參數,定義時在類型前面加上三個點
	for index, s := range ss {//做爲slice來遍歷可變參數
		fmt.Printf("index : %d, value : %s \n", index, s)//index和s都做爲可變參數來使用
	}
}

/*
結果:
index : 0, value : jack 
index : 1, value : rose 
index : 2, value : tom 
index : 3, value : jerry 
[jack rose tom jerry]
*
/

總結

Go 中的流程控制大體上就這麼多,平時項目中使用的也是很是多的,特別是對便利集合時,很是的方便。相信你親自體驗後也會讚不絕口的。

同時也順帶解釋了一下可變參數,結合着slice和流程控制也能對這個可變參數有一個更深的瞭解。

源碼能夠經過'github.com/souyunkutech/gosample'獲取。

關注咱們的「微信公衆號」


首發微信公衆號:Go技術棧,ID:GoStack

版權歸做者全部,任何形式轉載請聯繫做者。

做者:搜雲庫技術團隊 出處:https://gostack.souyunku.com/2019/04/29/if-for-switch-select

相關文章
相關標籤/搜索