1. Tổng quan.
Trong ngôn ngữ lập trình Go, vòng lặp (loop) được sử dụng để lặp code nhiều lần dựa trên một điều kiện hay một tập hợp giá trị..
Câu lệnh for là cách duy nhất để thực hiện vòng lặp trong Go. Cú pháp:
for statement1; statement2; statement3 {
// Code sẽ được thực thi cho mỗi lần lặp
}
statement1
: Khởi tạo giá trị của biến đếm cho vòng lặp.statement2
: Được sử dụng để so sánh kết quả cho mỗi lần lặp. Nếu kết quả là TRUE vòng lặp tiếp tục; nếu là FALSE vòng lặp kết thúc.statement3
: Tăng giá trị của biến đếm sau mỗi lần lặp.
Ví dụ 1 – Cơ bản về cách sử dụng câu lệnh for.
package main
import "fmt"
func main() {
// Vòng lặp từ 0 đến 4
for i := 0; i < 5; i++ {
fmt.Println(i)
}
}
Kết quả:
0
1
2
3
4
Trong ví dụ này:
for i := 0; i < 5; i++
: Câu lệnh for khởi tạo biếni
với giá trị 0 và lặp cho đến khii
không còn nhỏ hơn 5. Sau mỗi lần lặp, giá trị củai
được tăng lên 1 (i++
).fmt.Println(i)
: Mỗi lần lặp, giá trị củai
được in ra màn hình.
Do đó, kết quả của chương trình là in ra các số từ 0 đến 4.
Phần giải thích thêm ý nghĩa của các phần trong câu lệnh for
trong ví dụ 1.
i := 0;
: Phần này khởi tạo biến đếmi
và gán giá trị khởi đầu là 0. Điều này được thực hiện một lần duy nhất khi bắt đầu vòng lặp.i < 5;
: Phần này là điều kiện kiểm tra. Vòng lặp sẽ tiếp tục chạy miễn là giá trị củai
nhỏ hơn 5. Nếu điều kiện này không đúng vòng lặp sẽ kết thúc.i++
: Phần này là bước tăng giá trị của biến đếmi
sau mỗi lần lặp. Trong trường hợp này giá trị củai
tăng lên 1 sau mỗi lần lặp.
Kết hợp những phần này, vòng lặp sẽ bắt đầu với i
có giá trị là 0, tiếp tục lặp cho đến khi i
đạt đến giá trị 5 (vì điều kiện kiểm tra là i < 5
) và sau mỗi lần lặp thì giá trị của i
tăng lên 1. Do đó, vòng lặp sẽ in ra các số từ 0 đến 4.
Ví dụ 2 – Cách sử dụng câu lệnh for để in ra các số từ 0 đến 100 với bước nhảy là 10.
package main
import "fmt"
func main() {
// Vòng lặp từ 0 đến 100, tăng giá trị của i lên 10 sau mỗi lần lặp
for i := 0; i <= 100; i += 10 {
fmt.Println(i)
}
}
Kết quả:
0
10
20
30
40
50
60
70
80
90
100
Trong ví dụ này:
for i := 0; i <= 100; i += 10
: Câu lệnh for bắt đầu với biến đếmi
có giá trị khởi đầu là 0. Vòng lặp sẽ tiếp tục khii
không vượt quá giá trị 100. Sau mỗi lần lặp, giá trị củai
tăng lên 10 (i += 10
).fmt.Println(i)
: Mỗi lần lặp, giá trị củai
sẽ được in ra màn hình.
Do đó, vòng lặp này sẽ in ra các số từ 0 đến 100 với bước nhảy là 10.
Giải thích thêm về ví dụ này.
i := 0;
: Phần này khởi tạo biến đếmi
và gán giá trị khởi đầu là 0. Điều này được thực hiện một lần duy nhất khi bắt đầu vòng lặp.i <= 100;
: Phần này là điều kiện kiểm tra. Vòng lặp sẽ tiếp tục chạy miễn là giá trị củai
nhỏ hơn hoặc bằng 100. Nếu điều kiện này không đúng, vòng lặp sẽ kết thúc.i += 10
: Phần này là bước tăng giá trị của biến đếmi
sau mỗi lần lặp. Trong trường hợp này, giá trị củai
tăng lên 10 sau mỗi lần lặp.
Kết hợp những phần này, vòng lặp sẽ bắt đầu với i
có giá trị là 0, tiếp tục lặp cho đến khi i
đạt đến hoặc vượt quá giá trị 100 và sau mỗi lần lặp, giá trị của i
tăng lên 10. Do đó, vòng lặp sẽ in ra các số từ 0 đến 100 với bước nhảy là 10.
2. Vòng lặp với câu lệnh continue
.
Trong nhiều ngôn ngữ lập trình, bao gồm cả Go, continue
là một câu lệnh được sử dụng để bỏ qua phần còn lại của một lần lặp trong vòng lặp và chuyển đến lần lặp tiếp theo. Nó giúp bỏ qua phần code dưới continue
và tiếp tục với bước tiếp theo của vòng lặp.
Ví dụ 1.
Trong một vòng lặp for
, bạn có thể sử dụng continue
để bỏ qua các bước xử lý bên dưới và chuyển đến lần lặp tiếp theo:
for i := 0; i < 5; i++ {
if i == 2 {
continue // Bỏ qua phần code dưới và chuyển đến lần lặp tiếp theo
}
fmt.Println(i)
}
Trong ví dụ này, khi i
bằng 2, câu lệnh continue
sẽ được thực hiện, và phần code in giá trị của i
sẽ không được thực hiện cho i
bằng 2, nhưng sẽ tiếp tục với các giá trị khác của i
.
Ví dụ 2.
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
if i == 3 {
continue
}
fmt.Println(i)
}
}
Kết quả:
0
1
2
4
Trong ví dụ này:
for i := 0; i < 5; i++
: Câu lệnh for khởi tạo biến đếmi
và thực hiện vòng lặp từ 0 đến 4.if i == 3 { continue }
: Nếu giá trị củai
là 3, câu lệnhcontinue
sẽ được thực hiện, bỏ qua lần lặp này và chuyển đến lần lặp tiếp theo trong vòng lặp.fmt.Println(i)
: Mỗi lần lặp, giá trị củai
được in ra màn hình.
Vì vậy, kết quả của chương trình là in ra các số từ 0 đến 4, nhưng giá trị 3 đã được bỏ qua do sử dụng câu lệnh continue
.
3. Vòng lặp với câu lệnh break.
Câu lệnh break
được sử dụng để kết thúc thực thi của vòng lặp.
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
if i == 3 {
break
}
fmt.Println(i)
}
}
Kết quả:
0
1
2
Trong ví dụ này:
for i := 0; i < 5; i++
: Câu lệnh for khởi tạo biến đếmi
và thực hiện vòng lặp từ 0 đến 4.if i == 3 { break }
: Nếu giá trị củai
là 3, câu lệnhbreak
sẽ được thực hiện, kết thúc ngay lập tức vòng lặp và thoát khỏi nó.fmt.Println(i)
: Mỗi lần lặp, giá trị củai
được in ra màn hình. Tuy nhiên, do có câu lệnhbreak
khii
đạt đến 3, nó chỉ in ra các số từ 0 đến 2.
Lưu ý: continue
và break
thường được sử dụng với các điều kiện để kiểm soát quá trình lặp.
4. Sử dụng vòng lặp lồng nhau.
Dưới đây là ví dụ về việc sử dụng vòng lặp lồng nhau, tức là đặt một vòng lặp bên trong một vòng lặp khác.
package main
import "fmt"
func main() {
adj := [2]string{"big", "tasty"}
fruits := [3]string{"apple", "orange", "banana"}
for i := 0; i < len(adj); i++ {
for j := 0; j < len(fruits); j++ {
fmt.Println(adj[i], fruits[j])
}
}
}
Kết quả:
big apple
big orange
big banana
tasty apple
tasty orange
tasty banana
Trong ví dụ này:
for i := 0; i < len(adj); i++
: Vòng lặp “outer loop” chạy qua các phần tử của mảngadj
.for j := 0; j < len(fruits); j++
: Vòng lặp “inner loop” chạy qua các phần tử của mảngfruits
. Điều này xảy ra mỗi khi “outer loop” thực hiện một lần lặp.fmt.Println(adj[i], fruits[j])
: Mỗi lần “inner loop” thực hiện một lần lặp, in ra kết hợp của phần tử từ mảngadj
vàfruits
.
Vậy nên, kết quả là tất cả các kết hợp có thể được tạo ra bằng cách ghép phần tử từ adj
và fruits
theo thứ tự. Trong trường hợp này, chương trình in ra tất cả các kết hợp giữa các tính từ trong adj
và các loại trái cây trong fruits
.
5. Vòng lặp sử dụng range
.
range
được sử dụng để dễ dàng lặp qua một mảng (array), một slice hoặc một bản đồ (map) trong ngôn ngữ lập trình Go. Nó trả về cả chỉ số (index) và giá trị của mỗi phần tử.
Dưới đây là cách sử dụng range
:
for index, value := range array|slice|map {
// Code sẽ được thực thi cho mỗi lần lặp
}
Tiếp theo là một số ví dụ về cách sử dụng range
:
Ví dụ 1 – Với Array hoặc Slice:
arr := [3]int{1, 2, 3}
for index, value := range arr {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
Ví dụ 2 – Sử dụng range
với slice:
slice := []string{"apple", "banana", "cherry"}
for index, value := range slice {
fmt.Printf("Index: %d, Value: %s\n", index, value)
}
Ví dụ 3 – Với Map:
myMap := map[string]int{"one": 1, "two": 2, "three": 3}
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
Ví dụ 4 – Khi bạn sử dụng range
, bạn có thể chọn sử dụng hoặc không sử dụng chỉ số hoặc giá trị bằng cách đặt dấu gạch chéo (_
) để bỏ qua giá trị bạn không quan tâm.
for _, value := range myMap {
fmt.Printf("Value: %d\n", value)
}
Ví dụ 5 – In ra chỉ số index và giá trị của các loại trái cây trong mảng fruits
.
package main
import "fmt"
func main() {
fruits := [3]string{"apple", "orange", "banana"}
for idx, val := range fruits {
fmt.Printf("%v\t%v\n", idx, val)
}
}
Kết quả:
0 apple
1 orange
2 banana
Trong ví dụ này:
for idx, val := range fruits
: Vòng lặp sử dụngrange
để lặp qua mảngfruits
. Với mỗi lần lặp,idx
sẽ chứa chỉ số (index) của phần tử vàval
sẽ chứa giá trị của phần tử.fmt.Printf("%v\t%v\n", idx, val)
: Mỗi lần lặp, code này sẽ in ra cả chỉ số và giá trị của phần tử. Do đó, kết quả là in ra chỉ số index và giá trị của các loại trái cây trong mảngfruits
.
Như vậy, range
là một công cụ rất tiện lợi cho việc lặp qua các phần tử trong các cấu trúc dữ liệu trong Go.
6. Bỏ qua chỉ số (index) bằng dấu _ (gạch chân).
Trong ví dụ này, dấu gạch chân _
được sử dụng để bỏ qua chỉ số (index), chỉ lấy giá trị của mỗi phần tử khi sử dụng range
.
package main
import "fmt"
func main() {
fruits := [3]string{"apple", "orange", "banana"}
for _, val := range fruits {
fmt.Printf("%v\n", val)
}
}
Kết quả.
apple
orange
banana
for _, val := range fruits
: Dấu gạch chân_
được sử dụng để bỏ qua chỉ số (index), chỉ lấy giá trị của phần tử. Với mỗi lần lặp,val
sẽ chứa giá trị của phần tử.fmt.Printf("%v\n", val)
: Mỗi lần lặp, code này sẽ in ra giá trị của phần tử. Do đó, kết quả là in ra giá trị của các loại trái cây trong mảngfruits
mà không in ra chỉ số.
7. Bỏ qua giá trị của mỗi phần tử bằng dấu _ (gạch chân).
Trong ví dụ này, dấu gạch chân _
được sử dụng để bỏ qua giá trị của mỗi phần tử, chỉ lấy chỉ số (index) khi sử dụng range
.
package main
import "fmt"
func main() {
fruits := [3]string{"apple", "orange", "banana"}
for idx, _ := range fruits {
fmt.Printf("%v\n", idx)
}
}
Kết quả.
0
1
2
for idx, _ := range fruits
: Dấu gạch chân_
được sử dụng để bỏ qua giá trị của phần tử, chỉ lấy giá trị của chỉ số (index). Với mỗi lần lặp,idx
sẽ chứa chỉ số của phần tử.fmt.Printf("%v\n", idx)
: Mỗi lần lặp, code này sẽ in ra chỉ số của phần tử. Do đó, kết quả là in ra chỉ số của các loại trái cây trong mảngfruits
mà không in ra giá trị.
8. Một ví dụ thực tế.
- Mình sử dụng Go viết một chương trình kiểm tra kết nối mạng bằng ICMP với quy trình như sau:
- Tạo một list chứa danh sách các địa chỉ IP cần kiểm tra.
- Vòng lặp qua từng địa chỉ IP trong danh sách.
- Thực hiện lệnh ping và lấy kết quả đầu ra.
- Kiểm tra lỗi trước khi xử lý đầu ra.
- Chuyển đổi đầu ra thành chuỗi.
- Sử dụng regex để tìm kiếm giá trị “0%” trong đoạn văn bản.
- Kiểm tra nếu có matches và lấy giá trị tương ứng.
package main
import (
"os/exec"
"regexp"
"fmt"
)
func main() {
// Danh sách các địa chỉ IP cần kiểm tra
ipAddresses := []string{"8.8.8.8", "1.1.1.1", "8.8.8.7"}
// Vòng lặp qua từng địa chỉ IP trong danh sách
for _, ipaddr := range ipAddresses {
// Thực hiện lệnh ping và lấy kết quả đầu ra
var count string = "1"
cmd := exec.Command("ping", "-c", count, ipaddr)
out, err := cmd.CombinedOutput()
// Kiểm tra lỗi trước khi xử lý đầu ra
if err != nil {
fmt.Printf("Error while pinging %s: %s\n", ipaddr, err)
continue
}
// Chuyển đổi đầu ra thành chuỗi
output := string(out)
// Sử dụng regex để tìm kiếm giá trị "0%" trong đoạn văn bản
re := regexp.MustCompile(`(\d+)% packet loss`)
matches := re.FindStringSubmatch(output)
// Kiểm tra nếu có matches và lấy giá trị tương ứng
if len(matches) > 1 {
packetLoss := matches[1]
fmt.Printf("Check IP: %s, Packet Loss: %s\n", ipaddr, packetLoss)
} else {
fmt.Printf("Không tìm thấy thông tin về packet loss cho IP: %s\n", ipaddr)
}
}
}
Và đây là kết quả.
$ go run main.go
Check IP: 8.8.8.8, Packet Loss: 0
Check IP: 1.1.1.1, Packet Loss: 0
Error while pinging 8.8.8.7: exit status 1