Bài 5.2. Python với nested loop

Bài 5.2. Python với nested loop
Chào mừng bạn đến với bài học về vòng lặp lồng nhau (nested loops) trong Python! Nếu bạn đã làm quen với các vòng lặp cơ bản như for
và while
, thì nested loops chính là bước tiếp theo để bạn mở khóa khả năng xử lý các tác vụ phức tạp hơn, đặc biệt là khi làm việc với dữ liệu có cấu trúc đa chiều hoặc cần lặp lại một quy trình nhiều lần theo những cách khác nhau.
Hãy tưởng tượng bạn có một chiếc đồng hồ. Kim giây quay một vòng đầy đủ (60 bước) cho mỗi phút trôi qua. Kim phút cũng quay một vòng đầy đủ (60 bước) cho mỗi giờ trôi qua. Đó chính là ý tưởng cốt lõi của nested loops: một vòng lặp chạy hoàn toàn bên trong một lần lặp của vòng lặp khác.
Nested Loop là gì?
Đơn giản là một vòng lặp nằm bên trong một vòng lặp khác.
- Vòng lặp bên ngoài (outer loop) sẽ chạy trước.
- Với mỗi một lần lặp của vòng lặp bên ngoài, vòng lặp bên trong (inner loop) sẽ chạy hoàn tất toàn bộ chu trình của nó.
- Sau khi vòng lặp bên trong chạy xong, vòng lặp bên ngoài mới tiếp tục lần lặp tiếp theo của nó.
Điều này tạo ra một hiệu ứng nhân lên về số lần thực thi code bên trong vòng lặp sâu nhất. Nếu vòng lặp ngoài chạy N
lần và vòng lặp trong chạy M
lần cho mỗi lần lặp của vòng ngoài, thì đoạn code bên trong vòng lặp trong sẽ thực thi tổng cộng N * M
lần.
Cú pháp Nested Loop
Bạn có thể lồng các loại vòng lặp khác nhau (ví dụ: for
trong while
hoặc ngược lại), nhưng phổ biến nhất là lồng hai vòng lặp for
với nhau.
Cú pháp lồng vòng lặp for
:
for bien_lap_ngoai in day_lap_ngoai:
# Code thực thi ở vòng lặp ngoài (trước vòng lặp trong)
print(f"Bắt đầu vòng lặp ngoài với giá trị: {bien_lap_ngoai}") # Ví dụ
for bien_lap_trong in day_lap_trong:
# Code thực thi ở vòng lặp trong
# Khối code này sẽ chạy M lần cho mỗi lần chạy của vòng lặp ngoài
print(f" -> Vòng lặp trong với giá trị: {bien_lap_trong}") # Ví dụ
# Code thực thi ở vòng lặp ngoài (sau khi vòng lặp trong kết thúc)
print(f"Kết thúc vòng lặp trong cho giá trị ngoài: {bien_lap_ngoai}") # Ví dụ
print("-" * 20) # Phân cách cho dễ nhìn
Cú pháp lồng vòng lặp while
:
# Khởi tạo biến điều kiện cho vòng lặp ngoài
i_ngoai = 0
while dieu_kien_ngoai(i_ngoai):
# Code vòng lặp ngoài
# Khởi tạo/reset biến điều kiện cho vòng lặp trong *mỗi lần* lặp ngoài
j_trong = 0
while dieu_kien_trong(j_trong):
# Code vòng lặp trong
# Cập nhật biến điều kiện trong
j_trong += 1
# Cập nhật biến điều kiện ngoài
i_ngoai += 1
# Code vòng lặp ngoài khác
Hãy cùng đi sâu vào các ví dụ để thấy rõ sức mạnh và cách ứng dụng của nested loops!
Ví dụ Minh Họa
Ví dụ 1: In Tọa độ đơn giản
Đây là ví dụ cơ bản nhất để thấy cách hai vòng lặp tương tác. Chúng ta sẽ in ra các cặp tọa độ (x, y)
.
print("--- Ví dụ 1: In tọa độ ---")
for x in range(3): # Vòng lặp ngoài: x chạy từ 0 đến 2
print(f"Vòng lặp ngoài: x = {x}")
for y in range(2): # Vòng lặp trong: y chạy từ 0 đến 1
print(f" -> Vòng lặp trong: y = {y}. Tọa độ (x, y) = ({x}, {y})")
print(f"Kết thúc vòng lặp trong cho x = {x}\n")
# Output mong đợi:
# --- Ví dụ 1: In tọa độ ---
# Vòng lặp ngoài: x = 0
# -> Vòng lặp trong: y = 0. Tọa độ (x, y) = (0, 0)
# -> Vòng lặp trong: y = 1. Tọa độ (x, y) = (0, 1)
# Kết thúc vòng lặp trong cho x = 0
#
# Vòng lặp ngoài: x = 1
# -> Vòng lặp trong: y = 0. Tọa độ (x, y) = (1, 0)
# -> Vòng lặp trong: y = 1. Tọa độ (x, y) = (1, 1)
# Kết thúc vòng lặp trong cho x = 1
#
# Vòng lặp ngoài: x = 2
# -> Vòng lặp trong: y = 0. Tọa độ (x, y) = (2, 0)
# -> Vòng lặp trong: y = 1. Tọa độ (x, y) = (2, 1)
# Kết thúc vòng lặp trong cho x = 2
Giải thích:
- Vòng lặp ngoài bắt đầu với
x = 0
. - Vòng lặp trong bắt đầu chạy. Nó chạy hoàn toàn với
y = 0
, rồiy = 1
. - Vòng lặp trong kết thúc cho lần lặp
x = 0
. - Vòng lặp ngoài chuyển sang lần lặp tiếp theo,
x = 1
. - Vòng lặp trong lại bắt đầu từ đầu và chạy hoàn toàn với
y = 0
, rồiy = 1
. - Quá trình này lặp lại cho đến khi vòng lặp ngoài kết thúc (khi
x = 2
).
Bạn thấy đấy, vòng lặp y
(bên trong) đã chạy toàn bộ 2 lần của nó cho mỗi một lần chạy của vòng lặp x
(bên ngoài).
Ví dụ 2: Xử lý Ma trận (List of Lists)
Nested loops là công cụ cực kỳ hữu ích khi làm việc với cấu trúc dữ liệu 2 chiều như ma trận (thường được biểu diễn bằng list chứa các list khác trong Python).
print("\n--- Ví dụ 2: Xử lý Ma trận ---")
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
tong_cac_phan_tu = 0
so_hang = len(matrix) # Số lượng list con (hàng)
print("Duyệt qua ma trận:")
for i in range(so_hang): # Vòng lặp ngoài: duyệt qua từng hàng (list con)
so_cot = len(matrix[i]) # Số lượng phần tử trong hàng hiện tại (cột)
print(f"Hàng {i}: {matrix[i]}")
for j in range(so_cot): # Vòng lặp trong: duyệt qua từng phần tử trong hàng hiện tại
phan_tu = matrix[i][j]
print(f" Phần tử tại vị trí ({i}, {j}): {phan_tu}")
tong_cac_phan_tu += phan_tu
print(f"\nTổng tất cả các phần tử trong ma trận: {tong_cac_phan_tu}")
# Output mong đợi:
# --- Ví dụ 2: Xử lý Ma trận ---
# Duyệt qua ma trận:
# Hàng 0: [1, 2, 3]
# Phần tử tại vị trí (0, 0): 1
# Phần tử tại vị trí (0, 1): 2
# Phần tử tại vị trí (0, 2): 3
# Hàng 1: [4, 5, 6]
# Phần tử tại vị trí (1, 0): 4
# Phần tử tại vị trí (1, 1): 5
# Phần tử tại vị trí (1, 2): 6
# Hàng 2: [7, 8, 9]
# Phần tử tại vị trí (2, 0): 7
# Phần tử tại vị trí (2, 1): 8
# Phần tử tại vị trí (2, 2): 9
#
# Tổng tất cả các phần tử trong ma trận: 45
Giải thích:
- Vòng lặp ngoài (
for i in range(so_hang)
) duyệt qua các chỉ số củamatrix
. Mỗii
tương ứng với một hàng (một list con). - Vòng lặp trong (
for j in range(so_cot)
) duyệt qua các chỉ số của hàng hiện tại (matrix[i]
). Mỗij
tương ứng với một cột trong hàng đó. - Bằng cách sử dụng
matrix[i][j]
, chúng ta có thể truy cập từng phần tử riêng lẻ trong ma trận. - Trong ví dụ này, chúng ta in ra từng phần tử và cộng dồn chúng vào biến
tong_cac_phan_tu
.
Đây là cách tiếp cận tiêu chuẩn để truy cập mọi phần tử trong một cấu trúc dữ liệu 2 chiều.
Ví dụ 3: Vẽ Mẫu hình (Pattern)
Nested loops thường được dùng để tạo ra các mẫu hình văn bản thú vị. Hãy thử vẽ một tam giác vuông bằng dấu sao *
.
print("\n--- Ví dụ 3: Vẽ Tam giác ---")
chieu_cao = 5
for i in range(1, chieu_cao + 1): # Vòng lặp ngoài: kiểm soát số hàng
# Vòng lặp trong: kiểm soát số lượng '*' trên mỗi hàng
# Số lượng '*' bằng với số thứ tự của hàng hiện tại (i)
for j in range(i):
print("* ", end="") # In '*' và một khoảng trắng, không xuống dòng
print() # Xuống dòng sau khi in xong một hàng
# Output mong đợi:
# --- Ví dụ 3: Vẽ Tam giác ---
# *
# * *
# * * *
# * * * *
# * * * * *
Giải thích:
- Vòng lặp ngoài (
for i in range(1, chieu_cao + 1)
) chạychieu_cao
lần, tương ứng với số hàng của tam giác. Biếni
sẽ nhận các giá trị từ 1 đến 5. - Vòng lặp trong (
for j in range(i)
) có số lần lặp phụ thuộc vào giá trị hiện tại củai
từ vòng lặp ngoài.- Khi
i = 1
, vòng lặp trong chạyrange(1)
(tức là 1 lần,j=0
), in ra 1 dấu*
. - Khi
i = 2
, vòng lặp trong chạyrange(2)
(tức là 2 lần,j=0, 1
), in ra 2 dấu*
. - ...
- Khi
i = 5
, vòng lặp trong chạyrange(5)
(tức là 5 lần,j=0, 1, 2, 3, 4
), in ra 5 dấu*
.
- Khi
print("* ", end="")
in dấu sao và khoảng trắng nhưng không tự động xuống dòng.print()
được gọi sau khi vòng lặp trong kết thúc (nhưng vẫn bên trong vòng lặp ngoài) để xuống dòng, chuẩn bị cho hàng tiếp theo.
Sự phụ thuộc của vòng lặp trong vào biến của vòng lặp ngoài là chìa khóa để tạo ra các mẫu hình thay đổi theo từng hàng.
Ví dụ 4: Tạo Bảng cửu chương
Một ứng dụng kinh điển khác của nested loops là tạo bảng cửu chương.
print("\n--- Ví dụ 4: Bảng cửu chương ---")
for i in range(1, 10): # Vòng lặp ngoài: các số từ 1 đến 9
print(f"--- Bảng cửu chương {i} ---")
for j in range(1, 11): # Vòng lặp trong: nhân với các số từ 1 đến 10
ket_qua = i * j
print(f"{i} x {j} = {ket_qua}")
print("-" * 25) # Phân cách giữa các bảng cửu chương
# Output mong đợi: (Chỉ hiển thị một phần)
# --- Ví dụ 4: Bảng cửu chương ---
# --- Bảng cửu chương 1 ---
# 1 x 1 = 1
# 1 x 2 = 2
# ...
# 1 x 10 = 10
# -------------------------
# --- Bảng cửu chương 2 ---
# 2 x 1 = 2
# 2 x 2 = 4
# ...
# 2 x 10 = 20
# -------------------------
# ... (tiếp tục cho đến bảng 9)
Giải thích:
- Vòng lặp ngoài (
for i in range(1, 10)
) chọn số mà chúng ta muốn tạo bảng cửu chương (từ 1 đến 9). - Vòng lặp trong (
for j in range(1, 11)
) lặp qua các số từ 1 đến 10 để nhân với sối
hiện tại. - Bên trong vòng lặp trong, chúng ta thực hiện phép nhân
i * j
và in ra kết quả theo định dạng quen thuộc. - Sau khi vòng lặp trong hoàn thành (đã in đủ 10 phép nhân cho
i
), chúng ta in một dòng phân cách trước khi vòng lặp ngoài chuyển sang giá trịi
tiếp theo.
Lưu ý khi sử dụng Nested Loops
- Hiệu suất (Performance): Hãy cẩn thận khi sử dụng nested loops với các tập dữ liệu lớn. Như đã đề cập, số lần thực thi của code bên trong cùng tăng lên rất nhanh (ví dụ:
N*M
cho 2 vòng lặp,N*M*P
cho 3 vòng lặp). NếuN
,M
,P
lớn, chương trình có thể trở nên rất chậm. Đôi khi có những cách tối ưu hơn (ví dụ: sử dụng thư viện như NumPy cho ma trận, hoặc thay đổi thuật toán). - Độ phức tạp và Khả năng đọc (Readability): Code với quá nhiều vòng lặp lồng nhau (3, 4 hoặc nhiều hơn) có thể trở nên rất khó đọc và khó gỡ lỗi. Nếu bạn thấy mình cần lồng quá sâu, hãy cân nhắc việc tách logic thành các hàm nhỏ hơn, dễ quản lý hơn.
break
vàcontinue
trong Nested Loops: Các câu lệnhbreak
vàcontinue
chỉ ảnh hưởng đến vòng lặp gần nhất mà chúng nằm bên trong.break
sẽ thoát khỏi vòng lặp trong cùng. Vòng lặp ngoài sẽ tiếp tục chạy bình thường.continue
sẽ bỏ qua phần còn lại của lần lặp hiện tại của vòng lặp trong cùng và chuyển sang lần lặp tiếp theo của vòng lặp đó.
print("\n--- Lưu ý về break trong nested loop ---") for i in range(3): print(f"Outer loop: i = {i}") for j in range(5): if i == 1 and j == 2: print(f" Inner loop: Breaking at j = {j} when i = {i}") break # Chỉ thoát khỏi vòng lặp j print(f" Inner loop: j = {j}") # Code này vẫn chạy sau khi break vòng lặp trong print(f"End of inner loop for i = {i}")
Trong ví dụ trên, khi
i = 1
vàj = 2
,break
được thực thi. Nó chỉ dừng vòng lặpj
choi = 1
. Vòng lặpi
vẫn tiếp tục vớii = 2
.
Nested loops là một công cụ mạnh mẽ và cần thiết trong lập trình Python. Bằng cách hiểu rõ cách chúng hoạt động và thực hành qua các ví dụ, bạn sẽ có thể áp dụng chúng hiệu quả để giải quyết nhiều bài toán thú vị!
Comments