Giới thiệu
Kiểm soát luồng(Flow control statements) đóng vai trò quan trọng trong việc điều chỉnh cách thức thực thi của chương trình. Trong ngôn ngữ lập trình Go, có một số lệnh kiểm soát luồng cơ bản như for, if-else, switch và defer được sử dụng để quản lý luồng của chương trình. Phần này sẽ giúp bạn hiểu rõ hơn về cách sử dụng các lệnh này để viết mã Go hiệu quả và có cấu trúc.
Chúng ta sẽ đi sâu vào từng loại lệnh kiểm soát luồng, xem cú pháp, cấu trúc và cách sử dụng chúng thông qua các ví dụ cụ thể. Điều này sẽ giúp bạn hiểu rõ cách sử dụng mỗi lệnh và làm thế nào để áp dụng chúng trong quy trình phát triển phần mềm của bạn.
Nội dung
Vòng lặp for
- Go chỉ có một cấu trúc vòng lặp, đó là vòng lặp for.
Vòng lặp for cơ bản có ba thành phần được phân tách bởi dấu chấm phẩy: \
- Câu lệnh init: được thực thi trước vòng lặp đầu tiên
- Biểu thức điều kiện: được đánh giá trước mỗi lần lặp
- Câu lệnh post: được thực thi ở cuối mỗi lần lặp
Câu lệnh init thường là một câu khai báo biến ngắn gọn, và các biến được khai báo ở đó chỉ có thể được nhìn thấy trong phạm vi của câu lệnh for.
Vòng lặp sẽ dừng lặp khi biểu thức điều kiện đánh giá thành false.
Lưu ý: Khác với các ngôn ngữ khác như C, Java hoặc JavaScript, không có dấu ngoặc đơn bao quanh ba thành phần của câu lệnh for và các dấu ngoặc nhọn { } luôn được yêu cầu.package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) }
-
Câu lệnh init và câu lệnh post có thể có hoặc không(optional)
package main import "fmt" func main() { sum := 1 for ; sum < 10; { fmt.Println(sum) sum += sum } fmt.Println(sum) }
-
Nếu loại bỏ luôn cả dấu chấm phẩy ở giữa các câu lệnh, nó sẽ giống "while" trong ngôn ngữ C
package main import "fmt" func main() { sum := 1 for sum < 10 { sum += sum } fmt.Println(sum) }
-
Nếu bạn bỏ qua toàn bộ điều kiện, nó sẽ trở thành một vòng lặp vô hạn (và tất nhiên khi chạy sẽ lỗi "timeout running program" nếu chỉ có for không😄)
package main func main() { for { } }
-
For-each range loop thường được sử dụng trong slices, arrays, maps, channels hoặc strings (Mình sẽ giới thiệu các loại dữ liệu này ở bài sau)
strings := []string{"hello", "world"} for i, s := range strings { fmt.Println(i, s) }
Output:
0 hello 1 world
-
Exit a loop: Việc thoát khỏi vòng lặp của Go thì tương tự như cách làm của các ngôn ngữ khác như PHP, C hay Java, đó là dùng break và continue
If, If - else
-
Các câu lệnh if của Go giống như các vòng lặp for; biểu thức không cần phải được bao quanh bởi dấu ngoặc đơn ( ) nhưng bắt buộc phải có dấu ngoặc nhọn { }.
func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) }
-
Cũng giống như for, if cũng có thể bắt đầu với một câu lệnh ngắn(short statement) được thực thi trước khi kiểm tra điều kiện
func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim }
-
If và else: Các biến được khai báo bên trong câu lệnh if short statement cũng có sẵn bên trong bất kỳ khối else nào.
func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // không thể sử dụng biến v ở đây return lim }
Switch - Case
-
Một câu lệnh switch là cách viết ngắn gọn hơn để viết một chuỗi các câu lệnh if - else. Nó thực thi trường hợp đầu tiên mà giá trị của nó bằng với biểu thức điều kiện.
-
Câu lệnh switch trong Go tương tự như trong C, C++, Java, JavaScript, và PHP, ngoại trừ rằng Go chỉ thực thi trường hợp được chọn, không phải tất cả các trường hợp tiếp theo. Trong thực tế, câu lệnh break cần thiết ở cuối mỗi trường hợp trong những ngôn ngữ đó được cung cấp tự động trong Go. Một điểm khác quan trọng khác là các trường hợp của switch trong Go không cần phải là hằng số, và các giá trị liên quan không cần phải là số nguyên.
-
Các trường hợp của switch được đánh giá từ trên xuống dưới, dừng lại khi một trường hợp thành công.
package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.\n", os) } }
-
Switch không có điều kiện được gọi là switch true.Cấu trúc này có thể là một cách rõ ràng để viết các chuỗi if-then-else dài
package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } }
Defer
- Trì hoãn (defer) là một khái niệm khá mới trong điều khiển luồng. Nó cho phép một câu lệnh được gọi ra nhưng không thực thi ngay mà hoãn lại đến khi các lệnh xung quanh trả về kết quả.
Output:package main import "fmt" func main() { defer fmt.Println("world") // Trì hoãn in ra chữ "world" fmt.Println("hello") // In ra chữ "hello" }
hello world
- Các lệnh được gọi qua từ khóa defer sẽ được đưa vào một stack, tức là hoạt động theo cơ chế vào sau ra trước (last-in-first-out). Lệnh nào defer sau sẽ được thực thi trước, giống như xếp 1 chồng đĩa thì chiếc đĩa sau cùng (ở trên cùng) sẽ được lấy ra trước.
Output:package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) // In ra giá trị của biến i } fmt.Println("done") }
counting done 9 8 7 6 5 4 3 2 1 0
Kết bài
Trong bài thứ 3 này mình đã giới thiệu với các bạn những khái niệm cơ bản cũng như quan trọng nhất về Flow control statements trong golang. Bài tới mình sẽ giới thiệu về các types: structs, slices, and maps 😄
Nếu có thắc mắc hoặc phát hiện bài viết có thiếu sót gì, hãy comment cho mình biết để mình kịp thời chỉnh sửa cũng như cùng thảo luận về thắc mắc đó nhé 😉
Hi vọng những kiến thức này sẽ giúp ích được các bạn trong hành trình chinh phục Golang. Hẹn gặp lại ở những bài viết tiếp theo trong series Golang Essentials: Nền Tảng và Kiến Thức Cơ Bản. See ya!
Tham khảo: https://go.dev/tour/flowcontrol