M07 — Vòng lặp (for / while)

← 06 - Cấu trúc rẽ nhánh (if-elif-else) | Tiếp theo → 08 - List và Tuple

Mục tiêu module

  • Viết được vòng lặp for với range() và với dãy dữ liệu
  • Viết được vòng lặp while và tránh vòng lặp vô hạn
  • Dùng break, continue, else của vòng lặp
  • Dùng enumerate() và zip()

Thời lượng: ~8 giờ | Tuần: 10–11

Tuần 10: Mục 1–5 (for, range) | Tuần 11: Mục 6–11 (while, break, ứng dụng)


PHẦN 1 — TUẦN 10

1. Vì sao cần vòng lặp?

Ở 02 - Chương trình đầu tiên và cú pháp nền tảng bài 3, bạn đã viết 10 dòng print() cho bảng cửu chương. Với vòng lặp:

for i in range(1, 11):
    print(f"2 x {i} = {2 * i}")

2 dòng thay cho 10 dòng. Và nếu cần in tới 1000 thì vẫn 2 dòng.

Loop (vòng lặp) cho phép lặp lại một khối lệnh nhiều lần.

graph TD
    A[Bắt đầu] --> B{Còn phần tử<br/>để lặp?}
    B -->|Có| C[Thực hiện khối lệnh]
    C --> B
    B -->|Không| D[Thoát vòng lặp]

2. Vòng lặp for

2.1. Cú pháp

for <biến> in <dãy dữ liệu>:
    <khối lệnh lặp lại>
for ky_tu in "PYTHON":
    print(ky_tu)
P
Y
T
H
O
N

Ở mỗi vòng lặp, biến ky_tu lần lượt nhận giá trị "P", "Y", "T", "H", "O", "N".

2.2. Lặp qua list

danh_sach_kh = ["Nguyen Van A", "Tran Thi B", "Le Van C"]
 
for kh in danh_sach_kh:
    print(f"Xin chao {kh}")
Xin chao Nguyen Van A
Xin chao Tran Thi B
Xin chao Le Van C

for của Python là "for-each"

Trong C#/Java, for (int i = 0; i < 10; i++) là vòng lặp đếm. Python không có dạng này. for của Python luôn là for-each — duyệt qua từng phần tử của một dãy.

Về mặt tư duy, for của Python gần với CURSOR trong T-SQL: duyệt lần lượt từng bản ghi.


3. Hàm range()

range() tạo ra một dãy số nguyên — dùng khi cần lặp một số lần nhất định.

3.1. Ba dạng

Cú phápÝ nghĩaVí dụDãy sinh ra
range(stop)Từ 0 đến stop - 1range(5)0, 1, 2, 3, 4
range(start, stop)Từ start đến stop - 1range(2, 6)2, 3, 4, 5
range(start, stop, step)Có bước nhảyrange(0, 10, 2)0, 2, 4, 6, 8

stop KHÔNG được lấy

Giống như slicing ở 05 - Chuỗi ký tự (String), giá trị stop không nằm trong dãy.
range(5) sinh ra 5 số: 0, 1, 2, 3, 4 — không có số 5.

3.2. Ví dụ

for i in range(5):
    print(i, end=" ")
# 0 1 2 3 4
for i in range(1, 6):
    print(i, end=" ")
# 1 2 3 4 5
for i in range(0, 21, 5):
    print(i, end=" ")
# 0 5 10 15 20
# Đếm ngược — step âm
for i in range(10, 0, -1):
    print(i, end=" ")
# 10 9 8 7 6 5 4 3 2 1

3.3. range() không phải list

print(range(5))          # range(0, 5)   <- không phải [0,1,2,3,4]
print(list(range(5)))    # [0, 1, 2, 3, 4]
print(len(range(5)))     # 5

range() là một lazy sequence (dãy lười) — nó không tạo sẵn toàn bộ số trong bộ nhớ mà chỉ sinh ra khi cần. Nhờ vậy range(1_000_000_000) không làm tràn RAM.

3.4. Biến _ khi không dùng tới

# Khi không cần dùng biến đếm
for _ in range(3):
    print("Xin chao")

Dấu _ là quy ước Python cho “biến này tôi không dùng đến”.


4. Ví dụ ứng dụng for

4.1. Bảng cửu chương

so = int(input("Nhap so can in bang cuu chuong: "))
 
print(f"BANG CUU CHUONG {so}")
print("-" * 20)
for i in range(1, 11):
    print(f"{so} x {i:>2} = {so * i:>3}")

4.2. Tính tổng và trung bình

tong = 0
n = 10
 
for i in range(1, n + 1):
    tong += i
 
print(f"Tong tu 1 den {n} = {tong}")           # 55
print(f"Trung binh = {tong / n}")              # 5.5

4.3. Tính giai thừa

n = int(input("Nhap n: "))
giai_thua = 1
 
for i in range(1, n + 1):
    giai_thua *= i
 
print(f"{n}! = {giai_thua}")

4.4. Đếm ký tự

chuoi = input("Nhap mot cau: ")
so_nguyen_am = 0
 
for ky_tu in chuoi.lower():
    if ky_tu in "aeiou":
        so_nguyen_am += 1
 
print(f"So nguyen am: {so_nguyen_am}")

4.5. Vòng lặp lồng nhau — Bảng cửu chương đầy đủ

for i in range(2, 10):
    print(f"--- Bang {i} ---")
    for j in range(1, 11):
        print(f"{i} x {j:>2} = {i * j:>3}")
    print()

Vòng lặp ngoài chạy 8 lần (i từ 2→9), mỗi lần vòng trong chạy 10 lần → tổng 80 dòng.

4.6. Vẽ hình

n = 5
 
# Tam giác vuông
for i in range(1, n + 1):
    print("*" * i)
 
print()
 
# Tam giác cân
for i in range(1, n + 1):
    khoang_trang = " " * (n - i)
    sao = "*" * (2 * i - 1)
    print(khoang_trang + sao)
*
**
***
****
*****

    *
   ***
  *****
 *******
*********

5. enumerate() và zip()

5.1. enumerate() — Lấy cả chỉ số lẫn giá trị

danh_sach = ["Ban lam viec", "Ghe xoay", "Tu ho so"]
 
# ❌ Cách vụng về
for i in range(len(danh_sach)):
    print(f"{i + 1}. {danh_sach[i]}")
 
# ✅ Cách Python
for i, ten in enumerate(danh_sach, start=1):
    print(f"{i}. {ten}")
1. Ban lam viec
2. Ghe xoay
3. Tu ho so

enumerate() trả về từng cặp (chỉ số, giá trị). Tham số start quy định chỉ số bắt đầu (mặc định 0).

5.2. zip() — Ghép nhiều dãy song song

ten_hang = ["Ban lam viec", "Ghe xoay", "Tu ho so"]
so_luong = [3, 10, 2]
don_gia = [2_500_000, 850_000, 4_200_000]
 
tong_cong = 0
print(f"{'TEN HANG':<20}{'SL':>5}{'DON GIA':>14}{'THANH TIEN':>16}")
print("-" * 55)
 
for ten, sl, gia in zip(ten_hang, so_luong, don_gia):
    thanh_tien = sl * gia
    tong_cong += thanh_tien
    print(f"{ten:<20}{sl:>5}{gia:>14,}{thanh_tien:>16,}")
 
print("-" * 55)
print(f"{'TONG CONG':<39}{tong_cong:>16,}")
TEN HANG               SL       DON GIA      THANH TIEN
-------------------------------------------------------
Ban lam viec            3     2,500,000       7,500,000
Ghe xoay               10       850,000       8,500,000
Tu ho so                2     4,200,000       8,400,000
-------------------------------------------------------
TONG CONG                                     24,400,000

zip() dừng ở dãy ngắn nhất

a = [1, 2, 3, 4, 5]
b = ["x", "y"]
for i, j in zip(a, b):
    print(i, j)
# 1 x
# 2 y      <- dừng ở đây

5.3. Kết hợp enumerate và zip

for stt, (ten, sl) in enumerate(zip(ten_hang, so_luong), start=1):
    print(f"{stt}. {ten}: {sl}")

PHẦN 2 — TUẦN 11

6. Vòng lặp while

6.1. Cú pháp

while <điều kiện>:
    <khối lệnh lặp lại khi điều kiện còn đúng>
i = 1
while i <= 5:
    print(i)
    i += 1        # BẮT BUỘC — nếu quên sẽ lặp vô hạn
1
2
3
4
5

6.2. Ba thành phần bắt buộc

i = 1              # 1. KHỞI TẠO biến điều khiển
while i <= 5:      # 2. ĐIỀU KIỆN dừng
    print(i)
    i += 1         # 3. CẬP NHẬT biến điều khiển

Thiếu thành phần 3 → infinite loop (vòng lặp vô hạn), chương trình treo.

Vòng lặp vô hạn

i = 1
while i <= 5:
    print(i)      # In số 1 mãi mãi

Cách thoát: nhấn Ctrl + C trong terminal.

Luôn tự hỏi trước khi chạy: “Biến điều khiển có được cập nhật trong vòng lặp không? Điều kiện có bao giờ trở thành False không?“

6.3. Khi nào dùng for, khi nào dùng while?

Dùng forDùng while
Biết trước số lần lặpKhông biết trước số lần lặp
Duyệt qua dãy dữ liệu có sẵnLặp cho tới khi thỏa mãn điều kiện
Ví dụ: in bảng cửu chương, duyệt danh sáchVí dụ: chờ người dùng nhập đúng, đọc file tới hết
# ✅ for — biết trước 10 lần
for i in range(10):
    print(i)
 
# ✅ while — không biết người dùng nhập bao nhiêu lần mới đúng
mat_khau = ""
while mat_khau != "abc123":
    mat_khau = input("Nhap mat khau: ")
print("Dang nhap thanh cong")

6.4. Menu chương trình — ứng dụng kinh điển của while

while True:
    print()
    print("=" * 35)
    print("  HE THONG QUAN LY KHO")
    print("=" * 35)
    print("  1. Nhap kho")
    print("  2. Xuat kho")
    print("  3. Xem ton kho")
    print("  0. Thoat")
    print("=" * 35)
 
    lua_chon = input("Chon chuc nang: ").strip()
 
    if lua_chon == "1":
        print(">> Chuc nang nhap kho")
    elif lua_chon == "2":
        print(">> Chuc nang xuat kho")
    elif lua_chon == "3":
        print(">> Chuc nang xem ton kho")
    elif lua_chon == "0":
        print("Tam biet!")
        break                    # Thoát vòng lặp
    else:
        print("Lua chon khong hop le, vui long chon lai")

while True: tạo vòng lặp vô hạn có chủ đích, và thoát bằng break. Đây là mẫu (pattern) rất phổ biến.


7. break và continue

7.1. break — Thoát khỏi vòng lặp ngay

for i in range(1, 11):
    if i == 5:
        break
    print(i, end=" ")
# 1 2 3 4

Khi i == 5, break được thực hiện → thoát hoàn toàn khỏi vòng lặp.

Ứng dụng — tìm kiếm:

danh_sach = ["KH001", "KH002", "KH003", "KH004"]
can_tim = "KH003"
 
for i, ma in enumerate(danh_sach):
    if ma == can_tim:
        print(f"Tim thay {can_tim} tai vi tri {i}")
        break

7.2. continue — Bỏ qua phần còn lại, sang vòng tiếp theo

for i in range(1, 11):
    if i % 2 == 0:
        continue          # Bỏ qua số chẵn
    print(i, end=" ")
# 1 3 5 7 9

Ứng dụng — bỏ qua dữ liệu không hợp lệ:

du_lieu = ["100", "abc", "250", "", "300", "xyz"]
tong = 0
 
for gia_tri in du_lieu:
    if not gia_tri.isdigit():
        print(f"Bo qua gia tri khong hop le: '{gia_tri}'")
        continue
    tong += int(gia_tri)
 
print(f"Tong: {tong}")     # 650

7.3. break chỉ thoát MỘT cấp vòng lặp

for i in range(3):
    for j in range(3):
        if j == 1:
            break              # Chỉ thoát vòng lặp j
        print(i, j)
# 0 0
# 1 0
# 2 0

Muốn thoát cả 2 cấp, dùng biến cờ (flag):

tim_thay = False
for i in range(3):
    for j in range(3):
        if i == 1 and j == 1:
            tim_thay = True
            break
    if tim_thay:
        break
print(f"Dung tai i={i}, j={j}")

Cách gọn hơn là tách vòng lặp thành hàm rồi dùng return — học ở 10 - Hàm (Function).


8. else của vòng lặp (đặc trưng riêng của Python)

Python cho phép vòng lặp có mệnh đề else. Nó chạy khi vòng lặp kết thúc bình thường — tức là KHÔNG bị break.

danh_sach = ["KH001", "KH002", "KH003"]
can_tim = "KH999"
 
for ma in danh_sach:
    if ma == can_tim:
        print(f"Tim thay {can_tim}")
        break
else:
    print(f"Khong tim thay {can_tim}")
Khong tim thay KH999

Ứng dụng — kiểm tra số nguyên tố:

n = int(input("Nhap so can kiem tra: "))
 
if n < 2:
    print(f"{n} khong phai so nguyen to")
else:
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            print(f"{n} khong phai so nguyen to (chia het cho {i})")
            break
    else:
        print(f"{n} la so nguyen to")

Cách nhớ

Đọc for ... else là “for … nếu không tìm thấy thì”. Nhiều lập trình viên Python lâu năm cũng ít dùng cấu trúc này vì dễ gây nhầm lẫn. Bạn cần nhận biết nó khi đọc code người khác, còn khi tự viết thì có thể dùng biến cờ cho rõ ràng hơn.


9. Validate dữ liệu nhập — mẫu chuẩn

Đây là mẫu bạn sẽ dùng suốt sự nghiệp:

# Bắt nhập cho tới khi đúng
while True:
    nhap = input("Nhap tuoi (0-150): ").strip()
 
    if not nhap:
        print("Loi: Ban chua nhap gi ca")
        continue
 
    if not nhap.isdigit():
        print("Loi: Vui long chi nhap chu so")
        continue
 
    tuoi = int(nhap)
    if not 0 <= tuoi <= 150:
        print("Loi: Tuoi phai tu 0 den 150")
        continue
 
    break        # Dữ liệu hợp lệ, thoát vòng lặp
 
print(f"Tuoi cua ban la {tuoi}")

Sau khi học 11 - Xử lý lỗi (Exception), bạn sẽ viết gọn hơn bằng try/except.


10. Chương trình mẫu tổng hợp

"""Trò chơi đoán số — tổng hợp M02 đến M07."""
 
import random
 
# ---------- Cấu hình ----------
MIN_SO = 1
MAX_SO = 100
SO_LUOT_TOI_DA = 7
 
# ---------- Khởi tạo ----------
so_bi_mat = random.randint(MIN_SO, MAX_SO)
so_luot_da_dung = 0
lich_su = []
 
print("=" * 45)
print("TRO CHOI DOAN SO".center(45))
print("=" * 45)
print(f"Toi da nghi mot so tu {MIN_SO} den {MAX_SO}.")
print(f"Ban co {SO_LUOT_TOI_DA} luot de doan.")
print("=" * 45)
 
# ---------- Vòng chơi ----------
while so_luot_da_dung < SO_LUOT_TOI_DA:
    so_luot_con_lai = SO_LUOT_TOI_DA - so_luot_da_dung
    nhap = input(f"\n[Con {so_luot_con_lai} luot] Doan cua ban: ").strip()
 
    # Kiểm tra dữ liệu
    if not nhap.isdigit():
        print("  ! Vui long nhap mot so nguyen duong")
        continue
 
    doan = int(nhap)
    if not MIN_SO <= doan <= MAX_SO:
        print(f"  ! So phai nam trong khoang {MIN_SO}-{MAX_SO}")
        continue
 
    if doan in lich_su:
        print(f"  ! Ban da doan {doan} roi. Thu so khac di!")
        continue
 
    # Tính lượt
    so_luot_da_dung += 1
    lich_su.append(doan)
 
    # So sánh
    if doan == so_bi_mat:
        print(f"\n{'*' * 45}")
        print(f"CHINH XAC! So bi mat la {so_bi_mat}")
        print(f"Ban da doan dung sau {so_luot_da_dung} luot.")
        print(f"Lich su doan: {lich_su}")
        print("*" * 45)
        break
    elif doan < so_bi_mat:
        chenh_lech = so_bi_mat - doan
        goi_y = "hoi thap" if chenh_lech <= 10 else "qua thap"
        print(f"  -> So cua ban {goi_y}")
    else:
        chenh_lech = doan - so_bi_mat
        goi_y = "hoi cao" if chenh_lech <= 10 else "qua cao"
        print(f"  -> So cua ban {goi_y}")
else:
    # else của while: chạy khi vòng lặp kết thúc mà KHÔNG bị break
    print(f"\n{'=' * 45}")
    print("HET LUOT! Ban da thua.")
    print(f"So bi mat la: {so_bi_mat}")
    print(f"Lich su doan: {lich_su}")
    print("=" * 45)

Module random

import random nạp thư viện sinh số ngẫu nhiên có sẵn của Python. random.randint(a, b) trả về số nguyên ngẫu nhiên từ a đến b (bao gồm cả b — khác range()). Bạn sẽ học kỹ về import ở 12 - Module, Package và Thư viện chuẩn.


11. Thực hành bắt buộc

Bài 1 — Cơ bản với for

  1. In các số từ 1 đến 20
  2. In các số chẵn từ 1 đến 50
  3. In các số từ 100 xuống 1
  4. Tính tổng các số chia hết cho 3 trong khoảng 1–100
  5. Tính 1 + 1/2 + 1/3 + ... + 1/n với n nhập từ bàn phím

Bài 2 — Vẽ hình

Nhập n, vẽ các hình sau:

Hình 1 (n=4)      Hình 2 (n=4)      Hình 3 (n=4)
*                    *              ****
**                  ***             ****
***                *****            ****
****              *******           ****

Bài 3 — Đếm và thống kê

Nhập một chuỗi, đếm và in ra:

  • Số ký tự chữ cái
  • Số chữ số
  • Số khoảng trắng
  • Số ký tự khác

Bài 4 — Bảng lũy tiến tiền điện (dùng vòng lặp)

Viết lại bài tiền điện ở 06 - Cấu trúc rẽ nhánh (if-elif-else) nhưng dùng vòng lặp qua danh sách các bậc giá, thay vì viết if/elif dài.

Gợi ý: dùng 2 list song song rồi zip().

Bài 5 — Menu quản lý

Viết chương trình menu có 4 chức năng và thoát, dùng while True + break.

Bài 6 — Kiểm tra số nguyên tố

Nhập n, in ra tất cả số nguyên tố từ 2 đến n.

Bài 7 — Dãy Fibonacci

In ra 20 số đầu tiên của dãy Fibonacci (0, 1, 1, 2, 3, 5, 8, 13…).

Bài 8 — Máy tính liên tục

Chương trình liên tục hỏi phép tính (số 1, phép toán, số 2) và in kết quả, cho tới khi người dùng gõ q để thoát. Xử lý được chia cho 0.

Bài 9 — Đảo ngược số

Nhập một số nguyên, đảo ngược các chữ số không dùng chuỗi, chỉ dùng // và %.

Nhap so: 12345
Ket qua: 54321

Bài 10 — Máy đếm tiền

Nhập một số tiền, in ra cần bao nhiêu tờ mỗi mệnh giá (500k, 200k, 100k, 50k, 20k, 10k, 5k, 2k, 1k), ưu tiên tờ lớn trước.

Nhap so tien: 1287000
500,000 x 2
200,000 x 1
 50,000 x 1
 20,000 x 1
 10,000 x 1
  5,000 x 1
  2,000 x 1

12. Tự kiểm tra

  • Tôi viết được for với range() cả 3 dạng
  • Tôi nhớ range(stop) không bao gồm stop
  • Tôi biết khi nào dùng for, khi nào dùng while
  • Tôi biết 3 thành phần bắt buộc của while và cách tránh lặp vô hạn
  • Tôi phân biệt break và continue
  • Tôi biết enumerate() và zip()
  • Tôi viết được vòng lặp lồng nhau
  • Tôi viết được mẫu validate dữ liệu nhập bằng while True

13. Tài liệu chính thức

NguồnNội dungLink
Tutorial — for StatementsNhập môn vòng lặphttps://docs.python.org/3/tutorial/controlflow.html#for-statements
Tutorial — break, continue, elseĐiều khiển vòng lặphttps://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements
range()Tài liệu hàm rangehttps://docs.python.org/3/library/stdtypes.html#range
enumerate()Tài liệu enumeratehttps://docs.python.org/3/library/functions.html#enumerate
zip()Tài liệu ziphttps://docs.python.org/3/library/functions.html#zip
random moduleSinh số ngẫu nhiênhttps://docs.python.org/3/library/random.html

Tiếp theo → 08 - List và Tuple