Sunday, January 19, 2025

[Golang] Phần 14 – Go Loops

-

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ến i với giá trị 0 và lặp cho đến khi i không còn nhỏ hơn 5. Sau mỗi lần lặp, giá trị của i được tăng lên 1 (i++).
  • fmt.Println(i): Mỗi lần lặp, giá trị của i đượ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 đếm i 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ủa i 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 đếm i sau mỗi lần lặp. Trong trường hợp này giá trị của i 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 đếm i có giá trị khởi đầu là 0. Vòng lặp sẽ tiếp tục khi i không vượt quá giá trị 100. Sau mỗi lần lặp, giá trị của i tăng lên 10 (i += 10).
  • fmt.Println(i): Mỗi lần lặp, giá trị của i 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 đếm i 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ủa i 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 đếm i sau mỗi lần lặp. Trong trường hợp này, giá trị của i 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 đếm i và thực hiện vòng lặp từ 0 đến 4.
  • if i == 3 { continue }: Nếu giá trị của i là 3, câu lệnh continue 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ủa i đượ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 đếm i và thực hiện vòng lặp từ 0 đến 4.
  • if i == 3 { break }: Nếu giá trị của i là 3, câu lệnh break 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ủa i được in ra màn hình. Tuy nhiên, do có câu lệnh break khi i đạt đến 3, nó chỉ in ra các số từ 0 đến 2.

Lưu ý: continuebreak 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ảng adj.
    • for j := 0; j < len(fruits); j++: Vòng lặp “inner loop” chạy qua các phần tử của mảng fruits. Đ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ảng adjfruits.

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ừ adjfruits 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ụng range để lặp qua mảng fruits. 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ảng fruits.

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ảng fruits 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ảng fruits 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

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories