溫馨提示×

溫馨提示×

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

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

Go36-18-for和switch

發(fā)布時間:2020-07-04 20:27:08 來源:網(wǎng)絡(luò) 閱讀:341 作者:騎士救兵 欄目:編程語言

if語句、for語句和switch語句都屬于Go語言的基本流程控制語句。
下面主要討論for語句和switch語句,不過不講語法規(guī)則,而是一些要注意的細節(jié),就是“坑”。

帶range子句的for語句

下面會分別講解迭代變量和range表達式的問題。

迭代變量

對于不同種類的range表達式結(jié)果值,for語句的迭代變量的數(shù)量可以不同,下面是一個迭代變量的例子:

func main() {
    nums := []string{"A", "B", "C", "D", "E", "F"}
    for i := range nums {
        fmt.Println(i)
    }
}

當只有一個迭代變量的時候,數(shù)組、數(shù)組的指針、切片和字符串的元素值都是無處安放的,只能拿到按照從小到大順序給出的一個個索引值。
如果是兩個迭代變量,就可以去到元素值了:

for i, v := range nums {
    fmt.Println(i, v)
}

如果只要取元素只,可以用占位符:

for _, v := range nums {
    fmt.Println(v)
}

迭代數(shù)組和切片

這里主要講range表達式的特性,下面是例子:

package main

import "fmt"

func main() {
    nums := [...]int{1, 2, 3, 4, 5, 6}
    for i, e := range nums {
        if i == len(nums) -1 {
            nums[0] += e
            fmt.Println(e, nums[0])
        } else {
            nums[i+1] += e
            fmt.Println(e, nums[i+1])
        }

    }
    fmt.Println(nums)
}
/* 執(zhí)行結(jié)果
[7 3 5 7 9 11]
*/

首先要注意,這里要迭代的是一個數(shù)組(不是切片了)。
上面的代碼是在每次迭代的時候,都會去修改下一個元素的值,把下一個元素的值加上當前位置的元素值。問題是在下次迭代的時候,取到的值不是之前上次迭代后修改的值,而是數(shù)組原始的那個值。結(jié)論概括如下:

  • range表達式只會在for語句開始執(zhí)行時被求值一次,無論后邊會有多少次迭代
  • range表達式的求值結(jié)果會被復制,就是說,被迭代的對象是range表達式結(jié)果值的副本而不是原值

我的理解是,for i, e := range nums這句,拿去給range表達式處理的是nums的副本。
接下來把上面操作的數(shù)組改成切片,就是改成這樣:nums := []int{1, 2, 3, 4, 5, 6}。之前的數(shù)組是值類型,而現(xiàn)在切片是引用類型。再次運行后的結(jié)果就不一樣了。結(jié)果應該是:[22 3 6 10 15 21]。因為是引用類型,range表達式傳遞還是副本,不過這次是地址的副本,所以每次迭代對下一個元素的修改會在之后的迭代中取出上次修改后的值。

switch語句

在switch語句中,有switch表達式和case表達式。

判等操作

下面這段代碼會有編譯錯誤:

package main

import "fmt"

func main() {
    v := [...]int8{1, 2, 3, 4, 5, 6}
    switch 4 {
    case v[0], v[1]:
        fmt.Println("1 or 2")
    case v[2], v[3]:
        fmt.Println("3 or 4")
    case v[4], v[5]:
        fmt.Println("5 or 6")
    }
}

switch表達式需要做判等操作,switch表達式的結(jié)果類型,以及各個case表達式的結(jié)果類型是要求一樣的。因為,在Go語言中,只有類型相同的值之間才被允許進行判等操作。
上面的例子中,數(shù)組中元素的值是int8類型,而switch表達式的結(jié)果是數(shù)字常量,如果switch表達式的結(jié)果值是無類型的常量,則會被自動斷轉(zhuǎn)換成此種常量的默認類型值。這里的常量是整數(shù)4,會轉(zhuǎn)成int類型。然后兩個類型是不同的,所以無法通過編譯。

交換switch表達式和case表達式的類型
下面這段代碼,和上面的例子差不多,只是交換了switch和case里的類型:

func main() {
    v := [...]int8{1, 2, 3, 4, 5, 6}
    switch v[4] {
    case 1, 2:
        fmt.Println("1 or 2")
    case 3, 4:
        fmt.Println("3 or 4")
    case 5, 6:
        fmt.Println("5 or 6")
    }
}

這次switch表達式是int8類型,而case表達式是常量。如果case表達式的結(jié)果值是無類型的常量,則會被自動的轉(zhuǎn)換為switch表達式的結(jié)果類型。所以這里case表達式的結(jié)果會轉(zhuǎn)為int8類型,可以進行判斷操作。

小結(jié)
如果switch表達式的結(jié)果值是無類型的常量,那這個常量會被自動的轉(zhuǎn)換為此種常量的默認類型的值。
如果case表達式的結(jié)果值是無類型的常量,那么這個常量會被自動的轉(zhuǎn)換為switch表達式的結(jié)果類型。

最后還要注意,如果表達式的結(jié)果類型是接口類型,那么要小心檢查他們的動態(tài)值是否允許判等操作。因為這個錯誤不會被編譯器發(fā)現(xiàn),也就是不會造成編譯錯誤。但是這樣的后果更加嚴重,會在運行時引發(fā)Panic。

case表達式的約束

switch語句在case子句的選擇上是具有唯一性的,也就是switch語句不允許case表達式中的子表達式結(jié)果值存在相等的情況,不了這些值是否是在不同的case表達式中。下面的代碼就是因為有重復的值存在產(chǎn)生了編譯錯誤:

func main() {
    v := [...]int{1, 2, 3, 4, 5, 6}
    switch v[4] {
    case 1, 2 ,3:
        fmt.Println("1 or 2")
    case 3, 4, 7-1:
        fmt.Println("3 or 4")
    case 5, 6:
        fmt.Println("5 or 6")
    }
}

繞過約束
不過上面的這種約束只能在常量里發(fā)現(xiàn)。也就是說有辦法可以繞過這個限制:

package main

import "fmt"

func main() {
    v := [...]int{1, 2, 3, 4, 5, 6}
    switch v[4] {
    case v[0], v[1], v[2]:
        fmt.Println("1 or 2")
    case v[2], v[3], v[4]:
        fmt.Println("3 or 4")
    case v[3], v[4], v[5]:
        fmt.Println("5 or 6")
    }
}

上面用case表達式的常量換成了一個索引表達式,就繞過了編譯的檢查,并且程序也能正確的執(zhí)行并返回。

類型判斷的switch語句
上面這種繞過約束的方式,對這種switch語句是無效的。因為,類型switch語句中的case表達式的子表達式,都必須直接由類型字面量表示,而無法通過間接的方式表示。下面是編程會出錯的例子:

func main() {
    v := interface{}(byte(127))
    switch t := v.(type) {
    case uint8, uint16:
        fmt.Println("uint8 or uint16")
    case byte:
        fmt.Println("byte")
    default:
        fmt.Printf("%T\n", t)
    }
}

這里要知道byte類型是uint8類型的別名類型。這兩個本質(zhì)上是同一個類型,只是名稱不同。這種情況是無法通過編譯的,因為子表達式byte和uint8算是重復了。

類型判斷

switch還可以用來做類型判斷,這里直接給出例子:

package main

import "fmt"

func classifier(items ...interface{}) {
    for i, v := range items {
        switch v.(type) {
        case bool:
            fmt.Println("bool", i)
        case float64:
            fmt.Println("float64", i)
        case int:
            fmt.Println("int", i)
        case nil:
            fmt.Println("nil", i)
        case string:
            fmt.Println("string", i)
        default:
            fmt.Println("unknow", i)
        }
    }
}

func main() {
    classifier(1, "", nil, 1.234, true, int32(5))
}
向AI問一下細節(jié)

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

AI