Nhập môn Tư duy Lập trình/Vòng lặp while — cái gì đổi mỗi vòng, khi nào dừng
11/31
Bài 11 / 31~12 phútRẽ nhánh & lặpMiễn phí lượt xem

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 thangVòng lặp while
Bạn đang ở tầng mấyBiến điều khiển vòng lặp
Đứng ở chân cầu thang lúc đầuKhở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áiThân vòng lặp
Bước lên một bậcDòng cập nhật biến
Đứng yên không bướcVò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:

Sơ đồ luồng vòng lặp: hộp trên cùng dem gán 0 chú thích khởi tạo trước vòng lặp, mũi tên xuống hình thoi hỏi dem nhỏ hơn 3, nhánh sai đi sang phải tới hộp ra khỏi vòng lặp, nhánh đúng đi xuống hộp print(dem) là thân vòng lặp, tiếp tục xuống hộp nhấn màu xanh dem gán dem cộng 1 chú thích cập nhật thiếu dòng này là chạy mãi, rồi một mũi tên chạy vòng bên trái quay ngược lên hình thoi điều kiện

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âu tự kiểm trước khi chạy

Đọ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ử đoán

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.

Kết quả

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
Sắp lại thứ tự dòng

Đư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 = 3
  • dem = dem - 1
  • while dem < 0:
  • print("Het gio")
  • dem = dem + 1
  • while 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

📚 Đào sâu (không bắt buộc)

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 while là đúng loại biểu thức đúng/sai ở đó. Khác biệt duy nhất: if hỏi một lần, while hỏ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ì, for lo 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

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Vì sao bỏ dòng dem = dem + 1 là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".
  2. Q2
    Đoạn code có while dem < 3 và trong thân có dòng tong = 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?
  3. Q3
    Với so = 10while 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?
  4. Q4
    Vò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?
  5. Q5
    Trong 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 + 1 thay vì dem = dem - 1?
  6. Q6
    Khi 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

Đặt 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

Bài tiếp theo

for và danh sách — lặp qua một tập dữ liệu