Lệnh switch trong Go

Trong hướng dẫn này chúng ta sẽ tìm hiểu câu lệnh switch là gì, cách khai báo câu lệnh switch, các ví dụ về cách sử dụng câu lệnh switch case trong Go.

Câu lệnh switch là gì?

Switch là một câu lệnh điều kiện đánh giá một biểu thức và so sánh nó với một danh sách các kết quả phù hợp có thể có và thực thi khối mã tương ứng. Có thể coi đây là một cách tuyệt vời để thay thế các câu lệnh if else phức tạp.

Ví dụ về câu lệnh switch

Một chương trình ví dụ có giá trị hơn trăm lời nói. Hãy bắt đầu với một ví dụ đơn giản sẽ lấy số ngón tay làm đầu vào và xuất ra tên của ngón tay đó :). Ví dụ: 1 là ngón cái, 2 là ngón trỏ, v.v.

package main

import (  
    "fmt"
)

func main() {  
    finger := 4
    fmt.Printf("Finger %d is ", finger)
    switch finger {
    case 1:
        fmt.Println("Thumb")
    case 2:
        fmt.Println("Index")
    case 3:
        fmt.Println("Middle")
    case 4:
        fmt.Println("Ring")
    case 5:
        fmt.Println("Pinky")

    }
}

Chạy chương trình trong Playground

Trong chương trình trên, lệnh switch finger ở dòng số 10 so sánh giá trị của finger với mỗi câu lệnh case. Các trường hợp được đánh giá từ trên xuống dưới và trường hợp đầu tiên phù hợp với biểu thức được thực thi. Trong trường hợp này, finger có giá trị 4 và do đó sẽ in ra kết quả sau:

Finger 4 is Ring  

Các case không được trùng lặp

Các case trùng lặp có cùng giá trị là không được phép. Nếu bạn cố gắng chạy chương trình bên dưới, trình biên dịch sẽ báo lỗi ./prog.go:19:7: duplicate case 4 in switch previous case at ./prog.go:17:7

package main

import (  
    "fmt"
)

func main() {  
    finger := 4
    fmt.Printf("Finger %d is ", finger)
    switch finger {
    case 1:
        fmt.Println("Thumb")
    case 2:
        fmt.Println("Index")
    case 3:
        fmt.Println("Middle")
    case 4:
        fmt.Println("Ring")
    case 4: //duplicate case
        fmt.Println("Another Ring")
    case 5:
        fmt.Println("Pinky")

    }
}

Chạy chương trình trong Playground

Case mặc định

Chúng ta chỉ có 5 ngón tay trong bàn tay. Điều gì sẽ xảy ra nếu chúng ta nhập số ngón tay không chính xác? Đây là nơi mà trường hợp mặc định được sử dụng. Trường hợp mặc định sẽ được thực thi khi không có case nào khớp.

package main

import (  
    "fmt"
)

func main() {  
    switch finger := 8; finger {
    case 1:
        fmt.Println("Thumb")
    case 2:
        fmt.Println("Index")
    case 3:
        fmt.Println("Middle")
    case 4:
        fmt.Println("Ring")
    case 5:
        fmt.Println("Pinky")
    default: //default case
        fmt.Println("incorrect finger number")
    }
}

Chạy chương trình trong Playground

Trong chương trình trên finger8 và nó không khớp với bất kỳ case nào và do đó dòng incorrect finger number trong trường hợp mặc định được in. Lệnh default không cần thiết phải là trường hợp cuối cùng trong câu lệnh switch. Nó có thể hiện diện ở bất cứ đâu trong lệnh switch.

Bạn cũng có thể nhận thấy một thay đổi nhỏ trong khai báo của finger. Nó được khai báo trong chính câu lệnh switch. Câu lệnh switch có thể bao gồm một câu lệnh tùy chọn được thực thi trước khi biểu thức được đánh giá.

Trong dòng số 8, biến finger được khai báo đầu tiên và sau đó được sử dụng trong biểu thức. Phạm vi của biến finger trong trường hợp này được giới hạn trong khối lệnh switch.

Nhiều biểu thức trong case

Có thể gộp nhiều biểu thức trong một case bằng cách tách chúng bằng dấu phẩy.

package main

import (  
    "fmt"
)

func main() {  
    letter := "i"
    fmt.Printf("Letter %s is a ", letter)
    switch letter {
    case "a", "e", "i", "o", "u": //multiple expressions in case
        fmt.Println("vowel")
    default:
        fmt.Println("not a vowel")
    }
}

Chạy chương trình trong Playground

Chương trình trên tìm xem có phải letter là một nguyên âm hay không. Đoạn mã case "a", "e", "i", "o", "u": ở dòng số 11 khớp với bất kỳ nguyên âm nào. Vì i là một nguyên âm, chương trình này in kết quả như sau:

Letter i is a vowel  

Câu lệnh switch không có biểu thức

Biểu thức trong một câu lệnh switch là tùy chọn và nó có thể bị bỏ qua. Nếu biểu thức bị bỏ qua, lệnh switch được xem là switch true và mỗi biểu thức case được đánh giá là true và khối mã tương ứng được thực thi.

package main

import (  
    "fmt"
)

func main() {  
    num := 75
    switch { // expression is omitted
    case num >= 0 && num <= 50:
        fmt.Printf("%d is greater than 0 and less than 50", num)
    case num >= 51 && num <= 100:
        fmt.Printf("%d is greater than 51 and less than 100", num)
    case num >= 101:
        fmt.Printf("%d is greater than 100", num)
    }

}

Chạy chương trình trong Playground

Trong chương trình trên, biểu thức không có trong switch và do đó nó được coi là đúng và mỗi case được đánh giá. case num >= 51 && num <= 100: trong dòng số 12 là true và chương trình in ra kết quả sau:

75 is greater than 51 and less than 100  

Kiểu switch này có thể coi là phương án thay thế cho nhiều câu lệnh if else.

Câu lệnh fallthrough

Trong Go, điều khiển đi ra khỏi câu lệnh switch ngay sau khi một trường hợp được thực thi. Câu lệnh fallthrough được sử dụng để chuyển điều khiển cho câu lệnh đầu tiên của case có mặt ngay sau case đã được thực hiện.

Chúng ta hãy viết một chương trình để hiểu về lệnh fallthrough. Chương trình của chúng ta sẽ kiểm tra xem số đầu vào nhỏ hơn 50, 100 hay 200. Ví dụ: nếu chúng ta nhập 75, chương trình sẽ in rằng 75 nhỏ hơn cả 100 và 200. Chúng ta sẽ đạt được điều này bằng cách sử dụng fallthrough.

package main

import (  
    "fmt"
)

func number() int {  
        num := 15 * 5 
        return num
}

func main() {

    switch num := number(); { //num is not a constant
    case num < 50:
        fmt.Printf("%d is lesser than 50\n", num)
        fallthrough
    case num < 100:
        fmt.Printf("%d is lesser than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is lesser than 200", num)
    }

}

Chạy chương trình trong Playground

Biểu thức switch và case không chỉ cần hằng số. Chúng cũng có thể được đánh giá trong thời gian chạy. Trong chương trình trên num được khởi tạo giá trị trả về của hàm number() ở dòng số 14.

Điều khiển đến bên trong switch và case được đánh giá. case num < 100: trong dòng số 18 là true và chương trình sẽ in ra 75 is lesser than 100. Câu lệnh tiếp theo là fallthrough.

Khi gặp phải lệnh fallthrough, điều khiển sẽ chuyển đến câu lệnh đầu tiên của case tiếp theo và cũng được in ra 75 is lesser than 200. Đầu ra của chương trình là

75 is lesser than 100  
75 is lesser than 200  
Lệnh fallthrough nên là câu lệnh cuối cùng trong một case. Nếu nó hiện diện ở đâu đó ở giữa, trình biên dịch sẽ báo lỗi fallthrough statement out of place.

Fallthrough xảy ra ngay cả khi case được đánh giá là false

Có một sự tinh tế cần được xem xét khi sử dụng fallthrough. Fallthrough sẽ xảy ra ngay cả khi case được đánh giá là false.

Vui lòng xem xét chương trình sau đây.

package main

import (  
    "fmt"
)

func main() {  
    switch num := 25; { 
    case num < 50:
        fmt.Printf("%d is lesser than 50\n", num)
        fallthrough
    case num > 100:
        fmt.Printf("%d is greater than 100\n", num)     
    }

}

Chạy chương trình trong Playground

Trong chương trình trên, biến num là 25 nhỏ hơn 50 và do đó case ở dòng số 9 được đánh giá là true. Lệnh fallthrough có mặt ở dòng số 11. Case tiếp theo là case num > 100: tại dòng số 12 được đánh giá là false vì num < 100. Nhưng lệnh fallthrough không xem xét điều này. Fallthrough sẽ xảy ra ngay cả khi case được đánh giá là false.

Chương trình trên sẽ in ra kết quả sau:

25 is lesser than 50  
25 is greater than 100  

Vì vậy, hãy chắc chắn rằng bạn hiểu những gì bạn đang làm khi sử dụng lệnh fallthrough.

Một điều nữa là lệnh fallthrough không thể được sử dụng trong case cuối cùng của một switch vì không còn case nào để fallthrough. Nếu fallthrough có trong case cuối cùng, nó sẽ dẫn đến lỗi biên dịch sau.

cannot fallthrough final case in switch  

Câu lệnh break

Câu lệnh break có thể được sử dụng để kết thúc sớm một switch trước khi nó hoàn thành. Hãy chỉ sửa đổi ví dụ có sẵn ở trên để hiểu cách hoạt động của lệnh break.

Hãy thêm một điều kiện rằng nếu num nhỏ hơn 0 thì switch sẽ kết thúc.

package main

import (  
    "fmt"
)

func main() {  
    switch num := -5; {
    case num < 50:
        if num < 0 {
            break
        }
        fmt.Printf("%d is lesser than 50\n", num)
        fallthrough
    case num < 100:
        fmt.Printf("%d is lesser than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is lesser than 200", num)
    }

}

Chạy chương trình trong Playground

Trong chương trình trên num-5. Khi điều khiển đến câu lệnh if ở dòng số 10, điều kiện được thỏa mãn vì num < 0. Câu lệnh break kết thúc lệnh switch trước khi nó hoàn thành và chương trình không in bất cứ thứ gì :).

Thoát vòng lặp for bên ngoài

Khi lệnh switch nằm trong vòng lặp for, có thể một vài trường hợp cần kết thúc vòng lặp for sớm. Điều này có thể được thực hiện bằng cách gắn nhãn cho vòng lặp for và thoát khỏi vòng lặp for bằng cách sử dụng nhãn đó bên trong câu lệnh switch. Hãy xem ví dụ viết chương trình tạo một số chẵn ngẫu nhiên.

Chúng ta sẽ tạo một vòng lặp for vô hạn và sử dụng lệnh switch để xác định xem số ngẫu nhiên được tạo ra có phải là số chẵn hay không. Nếu nó là số chẵn, số đã tạo sẽ được in và vòng lặp for được kết thúc bằng cách sử dụng nhãn của nó. Function Intn của package rand được sử dụng để tạo ra các số ngẫu nhiên không âm.

package main

import (  
    "fmt"
    "math/rand"
)

func main() {  
randloop:  
    for {
        switch i := rand.Intn(100); {
        case i%2 == 0:
            fmt.Printf("Generated even number %d", i)
            break randloop
        }
    }

}

Chạy chương trình trong Playground

Trong chương trình trên, vòng lặp for được gắn nhãn randloop ở dòng số 9. Một số ngẫu nhiên được tạo từ 0 đến 99 (không bao gồm 100) bằng cách sử dụng function Intn ở dòng số 11. Nếu số được tạo là số chẵn, vòng lặp bị dừng ở dòng số 14 bằng cách sử dụng nhãn.

Chương trình này in ra kết quả

Generated even number 18  

Xin lưu ý rằng nếu câu lệnh break được sử dụng mà không có nhãn, sẽ chỉ thoát khỏi câu lệnh switch và vòng lặp sẽ tiếp tục chạy. Vì vậy, việc gắn nhãn vòng lặp và sử dụng nó trong câu lệnh break bên trong switch là cần thiết để thoát vòng lặp for bên ngoài.

Trong hướng dẫn tiếp theo, chúng ta sẽ tìm hiểu về mảng (array) trong Go.

Mảng trong Go
Trong hướng dẫn này, chúng ta sẽ tìm hiểu mảng (array) là gì? cách khai báo mảng một chiều, mảng đa chiều, duyệt mảng trong Go.

Như thường lệ, cảm ơn bạn đã đọc. Hãy chia sẻ những nhận xét và phản hồi có giá trị của bạn.

Nếu Comdy hữu ích và giúp bạn tiết kiệm thời gian làm việc

Bạn có thể vui lòng đưa Comdy vào whitelist của trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi trong việc trả tiền cho dịch vụ lưu trữ web để duy trì hoạt động của trang web.

Go
Bài Viết Liên Quan:
Phương thức (method) trong Go
Trung Nguyen 02/12/2021
Phương thức (method) trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu phương thức (method) trong Go là gì? Cú pháp khai báo phương thức, so sánh phương thức với hàm, ... trong Go.

Struct trong Go
Trung Nguyen 28/11/2021
Struct trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu struct là gì, cách khai báo và sử dụng một struct trong Go, struct ẩn danh, so sanh hai struct, ...

Con trỏ trong Go
Trung Nguyen 28/11/2021
Con trỏ trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách con trỏ (pointer) hoạt động trong Go và nó khác với con trỏ trong các ngôn ngữ khác như C và C++ như thế nào.

Chuỗi trong Go
Trung Nguyen 28/11/2021
Chuỗi trong Go

Chuỗi (string) xứng đáng được đề cập đặc biệt trong Go vì chúng khác biệt trong cách triển khai khi so sánh với các ngôn ngữ khác.