Vòng lặp while — cái gì đổi mỗi vòng, khi nào dừng
Làm lại một việc cho tới khi điều kiện hết đúng. Muốn nó dừng thì phải có thứ gì đó đổi mỗi vòng — quên đúng một dòng là máy chạy mãi không thôi.
TL;DR: while hỏi một điều kiện, và hỏi lại sau mỗi lần chạy xong thân vòng lặp — tài liệu tham chiếu Python mô tả nó là "lặp đi lặp lại việc kiểm tra biểu thức". Mọi vòng lặp dừng được đều có đủ ba phần: một biến được khởi tạo trước vòng lặp, một điều kiện hỏi về biến đó, và một dòng đổi chính biến đó bên trong thân. Thiếu phần thứ ba thì điều kiện cho ra cùng một câu trả lời mãi mãi và chương trình treo. Đếm số vòng thì đừng đoán: kẻ bảng trace, mỗi hàng một vòng, tới khi điều kiện thành sai.
Bạn cần in ra ba dòng. Viết ba lệnh print là xong. Cần in ra một nghìn dòng thì sao? Hay in ra đúng số dòng bằng số đơn hàng hôm nay, mà hôm nay bao nhiêu đơn thì lúc viết code bạn chưa biết?
Đây là lúc cần một cấu trúc bảo máy "làm lại việc này, tới khi nào tôi bảo dừng". Bài này dạy cấu trúc đó, và cách đếm chính xác nó chạy bao nhiêu vòng trước khi bạn chạy thử.
1. Analogy — cầu thang và câu hỏi ở mỗi bậc
Bạn đứng ở chân cầu thang và được giao một việc: "cứ bước lên, mỗi bậc nhìn quanh một cái, dừng khi tới tầng ba".
Bạn không nhìn biển "tầng ba" một lần rồi nhắm mắt bước mãi. Bạn hỏi lại ở mỗi bậc: đã tới tầng ba chưa? Chưa thì bước tiếp. Và cái làm bạn tới được tầng ba là mỗi lần bạn thật sự bước lên một bậc. Đứng yên nhìn quanh mãi thì câu trả lời không bao giờ đổi.
| Cầu thang | Vòng lặp while |
|---|---|
| Bạn đang ở tầng mấy | Biến điều khiển vòng lặp |
| Đứng ở chân cầu thang lúc đầu | Khởi tạo biến trước vòng lặp |
| "Đã tới tầng ba chưa?" hỏi ở mỗi bậc | Điều kiện, được tính lại mỗi vòng |
| Nhìn quanh một cái | Thân vòng lặp |
| Bước lên một bậc | Dòng cập nhật biến |
| Đứng yên không bước | Vòng lặp chạy mãi |
2. Ba phần của một vòng lặp dừng được
dem = 0
while dem < 3:
print(dem)
dem = dem + 1
print("Xong")
Output thật:
0
1
2
Xong
Biến mà điều kiện hỏi về — ở đây là dem — gọi là biến điều khiển của vòng lặp. Cả ba phần đều xoay quanh đúng nó: một chỗ khởi tạo nó, một chỗ hỏi về nó, một chỗ đổi nó.
Ba phần nằm ở ba chỗ khác nhau, và hình dưới cho thấy đường đi giữa chúng:

Cạnh quay lui bên trái là thứ phân biệt vòng lặp với rẽ nhánh. Ở bài 1, sau khi chạy xong một nhánh thì máy đi thẳng xuống dưới, không ngoái lại. Ở đây, chạy xong thân là quay lên hỏi lại câu hỏi cũ.
Tài liệu tham chiếu Python mô tả while đúng như vậy: nó "lặp đi lặp lại việc kiểm tra biểu thức và, nếu đúng, thi hành khối thứ nhất".
x = x + 1 không phải một mệnh đề toán học vô lý. Nó là ba nhịp: đọc giá trị hiện tại của x, tính ra giá trị mới, rồi ghi đè ô nhớ x bằng giá trị đó. Dòng dem = dem + 1 trong thân vòng lặp chính là chỗ ba nhịp ấy có việc để làm.
3. Điều gì làm vòng lặp chạy mãi không dừng?
Bỏ đúng một dòng khỏi đoạn code ở mục 2:
dem = 0
while dem < 3:
print(dem)
print("Xong")
dem được đặt bằng 0, rồi không dòng nào đổi nó nữa. Câu hỏi dem < 3 cho ra True ở vòng đầu, và vì dem vẫn bằng 0 ở vòng thứ hai, thứ ba, thứ một triệu, câu trả lời vĩnh viễn là True. Chương trình in số 0 mãi cho tới khi bạn tự bấm dừng nó. Dòng print("Xong") không bao giờ chạy.
Có một biến thể tinh vi hơn, và nó cắn nhiều hơn hẳn: cập nhật nhầm biến.
dem = 0
tong = 0
while dem < 3:
tong = tong + dem
tong = tong + 1
Ở đây có một dòng cộng thêm 1 hẳn hoi, nhìn qua rất giống một vòng lặp tử tế. Nhưng thứ được cộng là tong, còn thứ điều kiện đang hỏi là dem. dem không bao giờ đổi, nên vòng lặp không bao giờ dừng.
Đọc điều kiện, gạch chân tên biến trong đó. Rồi tìm trong thân vòng lặp: có dòng nào gán lại đúng cái tên vừa gạch chân không? Không có thì vòng lặp sẽ chạy mãi, không cần chạy thử cũng biết.
4. Thử đoán — vòng lặp này chạy mấy vòng?
so = 10
while so > 4:
so = so - 2
print(so)
Thân vòng lặp chạy bao nhiêu lần, và print cuối cùng in ra số mấy? Viết cả hai con số ra giấy. Nếu phân vân thì kẻ bảng: mỗi hàng một vòng, ghi giá trị so sau khi vòng đó chạy xong.
Thân chạy 3 lần, và output là:
4
Bảng trace từng vòng:
| Vòng | so trước khi hỏi | so > 4 ra gì | so sau khi chạy thân |
|---|---|---|---|
| 1 | 10 | True | 8 |
| 2 | 8 | True | 6 |
| 3 | 6 | True | 4 |
| — | 4 | False | vòng lặp dừng |
Chỗ dễ sai nhất là hàng cuối. Khi so bằng 4, câu hỏi so > 4 cho ra False — bốn không lớn hơn bốn. Nên vòng thứ tư không chạy, và so giữ nguyên 4.
Nếu bạn đoán 4 vòng và số cuối là 2, bạn vừa gặp lỗi lệch một đơn vị lần đầu trong khoá này. Bài 5 sẽ mổ kỹ nó.
5. Tới lượt bạn — sắp lại một vòng lặp
Bài tập dưới đây có sẵn các dòng nhưng bị xáo trộn, và trong kho có cả dòng thừa. Sắp chúng thành một chương trình đếm ngược từ 3 xuống 1 rồi in "Het gio".
Output mong đợi:
3
2
1
Het gio
Đưa từng dòng vào khung lời giải rồi xếp cho đúng thứ tự. Kho có thể chứa dòng thừa.
Kho dòng (7)
dem = 3dem = dem - 1while dem < 0:print("Het gio")dem = dem + 1while dem > 0:print(dem)
Lời giải của bạn (0/5)
Chưa có dòng nào. Bấm “Đưa vào” ở trên.
Gợi ý: Ba phần ở mục 2: khởi tạo phải nằm TRƯỚC while; dòng đổi biến phải nằm trong thân; và dòng in cuối cùng thì nằm ngoài, vì nó chỉ chạy một lần.
Hai dòng nhiễu trong kho không phải để làm khó cho vui. Một dòng cộng thay vì trừ — chọn nó thì dem chạy xa khỏi 0 và vòng lặp không bao giờ dừng. Một dòng hỏi ngược chiều — chọn nó thì điều kiện sai ngay từ đầu và thân không chạy lần nào.
6. Bẫy thường gặp khi viết while
❌ Nhầm 1 — quên hẳn dòng cập nhật. Vòng lặp chạy mãi, chương trình treo, và với người mới thì màn hình cuộn số vô tận trông y như máy hỏng.
✅ Viết dòng cập nhật ngay khi viết dòng while, trước cả khi viết thân. Coi nó là một phần của cú pháp, không phải thứ thêm sau.
❌ Nhầm 2 — cập nhật nhầm biến. Có dòng cộng, nhưng cộng vào biến khác biến mà điều kiện đang hỏi. Đây là bản khó chịu hơn của nhầm 1 vì nhìn qua code có vẻ đầy đủ.
✅ Gạch chân tên biến trong điều kiện, rồi tìm đúng cái tên đó trong thân.
❌ Nhầm 3 — đếm sai một vòng ở ranh giới. Với while so > 4, người mới hay tính cả vòng lúc so đã bằng 4.
✅ Kẻ bảng trace và viết riêng một hàng cuối cho lần hỏi thất bại. Vòng lặp luôn hỏi thêm một lần nữa sau vòng cuối cùng — chính lần hỏi đó làm nó dừng.
7. 📚 Đào sâu — định nghĩa while trong tài liệu tham chiếu
8. Compound statements — Python documentation — mục "The while statement" là nguồn của câu trích ở mục 2. Câu định nghĩa rất ngắn nhưng chứa đúng thứ bài này xoay quanh: chữ "repeatedly tests" nói rằng điều kiện được tính lại, chứ không phải tính một lần rồi nhớ kết quả.
Mục này cũng mô tả mệnh đề else của while — một tính năng khá riêng của Python, chạy khi vòng lặp kết thúc vì điều kiện thành sai. Khoá này không dùng tới, nhưng biết nó tồn tại thì khỏi ngơ ngác khi gặp trong code người khác.
8. Liên hệ các bài khác
- Bài 1 — Rẽ nhánh — điều kiện của
whilelà đúng loại biểu thức đúng/sai ở đó. Khác biệt duy nhất:ifhỏi một lần,whilehỏi lại sau mỗi vòng. - Bài 2 module trước — Biến và phép gán — khác biệt ở đây: cùng một phép gán ấy phải chạy lặp lại, và chính việc nó lặp mới làm điều kiện có ngày thành sai.
- Bài 4 — for và danh sách — khi đã biết trước cần lặp qua những gì,
forlo hộ bạn cả ba phần và cắt luôn nguy cơ chạy mãi. - Bài 5 — Lặp lồng nhau — lỗi lệch một đơn vị ở mục 4 được mổ kỹ tại đó.
9. Tóm tắt
- Đọc một
while, việc đầu tiên là tìm dòng cập nhật — chưa thấy thì đừng chạy thử, sửa ngay. - Thấy có dòng cộng rồi vẫn treo? Soi đúng tên biến bị cộng xem có khớp tên biến trong điều kiện không.
- Đếm được số vòng nhưng lệch một? Bảng trace của bạn nhiều khả năng thiếu hàng cuối — hàng cho lần hỏi thất bại.
- Chương trình cuộn số vô tận trên màn hình không phải máy hỏng, đó là vòng lặp thiếu một trong ba phần.
- Đang tự tay khởi tạo biến đếm rồi tự tăng, chỉ để đi hết một danh sách có sẵn? Bài sau có thứ làm hộ cả ba việc đó.
10. Tự kiểm tra
- Q1Vì sao bỏ dòng
dem = dem + 1làm vòng lặp chạy mãi? Giải thích bằng cơ chế, đừng chỉ nói "vì thiếu cập nhật". - Q2Đoạn code có
while dem < 3và trong thân có dòngtong = tong + 1. Vòng lặp này dừng không? Vì sao đây là lỗi khó thấy hơn việc quên hẳn dòng cập nhật? - Q3Với
so = 10vàwhile so > 4, thân trừ đi 2 mỗi vòng. Vì sao thân chạy 3 lần chứ không phải 4? - Q4Vòng lặp hỏi điều kiện tổng cộng bao nhiêu lần nếu thân chạy 3 lần? Vì sao con số này quan trọng khi trace?
- Q5Trong bài tập sắp dòng ở mục 5, chuyện gì xảy ra nếu bạn chọn dòng nhiễu
dem = dem + 1thay vìdem = dem - 1? - Q6Khi nào bạn nên chọn while, và có dấu hiệu nào cho thấy đáng lẽ nên dùng cấu trúc khác?
Bài tiếp theo: for và danh sách — lặp qua một tập dữ liệu
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên