Trong bài này
Lan bỏ xu vào hộp tiết kiệm. Hộp đang có 3 xu và Lan muốn đạt ít nhất 8 xu, mỗi lần thêm 2 xu. Lan cần lặp việc thêm xu nhưng chưa biết ngay phải thêm mấy lần.
while kiểm tra điều kiện trước mỗi lượt
Hộp xu bắt đầu với 3 xu. Mỗi lượt Lan thêm 2 xu, đến khi hộp có ít nhất 8 xu:
xu = 3
while xu < 8:
xu = xu + 2
print(xu)
print("Da du")5 7 9 Da du
Python kiểm tra xu < 8 trước mỗi lượt. Khi điều kiện đúng, các lệnh thụt vào chạy theo thứ tự. Sau đó Python kiểm tra lại điều kiện với giá trị xu mới. Khi điều kiện sai, chương trình tiếp tục ở lệnh sau vòng lặp.
Đây là vòng lặp có điều kiện (conditional loop). Dòng while cần dấu :; các lệnh thuộc vòng lặp phải thụt vào cùng mức. Nếu thiếu khối thụt vào, Python báo lỗi:
while 2 < 3:
print(2)Lỗi khi chạy, ở dòng 2
IndentationError: expected an indented block after 'while' statement on line 1
Giá trị được kiểm tra trước lượt đầu tiên. Nếu điều kiện đã sai, vòng lặp không chạy lượt nào:
xu = 9
while xu < 8:
xu = xu + 2
print(xu)9
Giá trị 9 không thay đổi. while phù hợp khi số lượt phụ thuộc vào điều kiện đang thay đổi. Nếu biết chính xác số lượt, for với range() cũng là lựa chọn thuận tiện.
Điều kiện là một biểu thức so sánh như đã học ở Bài 3. Kết quả của biểu thức quyết định có vào thân hay không. Bên trong thân, một lệnh gán có thể làm kết quả của lần kiểm tra kế tiếp thay đổi. Lệnh nằm ngoài thân không chạy xen giữa các lượt; nó đợi vòng kết thúc.
Kiểm traxu nhỏ hơn 8?ĐúngThêm 2 xu rồi kiểm tra lạiSaiTiếp tục sau vòng lặp
Cập nhật giúp vòng lặp đi đến điểm dừng
Một đồng hồ đếm ngược bắt đầu từ 3 và giảm mỗi lượt:
giay = 3
while giay > 0:
print(giay)
giay = giay - 1
print("Het gio")3 2 1 Het gio
Biến giay lần lượt nhận 3, 2, 1 rồi 0. Khi bằng 0, điều kiện sai. Bảng sau cho thấy giá trị sau từng lần giảm:
| Trước | Sau |
|---|---|
| 3 | 2 |
| 2 | 1 |
| 1 | 0 |
Một vòng lặp vô hạn (infinite loop) không tự kết thúc. Chẳng hạn, nếu bỏ lệnh giảm giay, điều kiện vẫn đúng và chương trình in mãi. Không chạy thử đoạn thiếu cập nhật đó; hãy xác định biến nào thay đổi và vì sao thay đổi ấy đưa điều kiện đến sai.
Chọn đúng hướng cập nhật cũng quan trọng. Với điều kiện giay > 0, tăng giay không đưa số dương về 0. Giá trị đầu, điều kiện và bước cập nhật phải cùng phù hợp với mục tiêu.
Nếu muốn in cả 0, có thể dùng điều kiện khác và vẫn giảm sau mỗi lượt:
giay = 2
while giay >= 0:
print(giay)
giay = giay - 12 1 0
break kết thúc vòng lặp ngay lập tức
Trong các số từ 1 đến 8, chương trình dừng khi gặp số chia hết cho 5:
so = 1
while so <= 8:
if so % 5 == 0:
print("Tim thay", so)
break
so = so + 1
print("Ket thuc")Tim thay 5 Ket thuc
break thoát khỏi vòng lặp gần nhất ngay khi chạy. Lệnh tăng phía sau break không chạy ở lượt đó. Chương trình tiếp tục ở print("Ket thuc") ngoài vòng lặp.
Không đặt break ngoài vòng lặp; Python không biết phải thoát vòng nào:
breakLỗi khi chạy, ở dòng 1
SyntaxError: 'break' outside loop
Với vòng lặp lồng nhau, break chỉ thoát vòng trong chứa nó. Vòng ngoài vẫn có thể tiếp tục:
for hang in range(1, 3):
for cot in range(1, 4):
if cot == 2:
break
print(hang, cot)1 1 2 1
Mỗi hàng chỉ in cột 1. Khi cot bằng 2, vòng cột dừng; sau đó vòng hàng chuyển sang lượt tiếp theo.
continue bỏ phần còn lại của lượt hiện tại
Lan ghi các số thứ tự từ 1 đến 5, trừ số 3:
so = 0
while so < 5:
so = so + 1
if so == 3:
continue
print(so)1 2 4 5
continue bỏ các lệnh còn lại của lượt hiện tại và quay về kiểm tra điều kiện. Khác với break, nó không kết thúc vòng lặp. Vì so đã tăng trước continue, lượt sau tiến đến số tiếp theo.
Nếu chuyển lệnh tăng xuống sau continue, lúc so bằng 3 chương trình sẽ lặp mãi ở giá trị đó. Hãy kiểm tra mọi đường đi qua thân vòng lặp đều có cách tiến đến điểm dừng.
continue cũng dùng được với for. Ví dụ này bỏ qua những số chẵn:
for so in range(1, 7):
if so % 2 == 0:
continue
print(so)1 3 5
Ở đây for tự chuyển sang giá trị kế tiếp trong range(). Với while, người viết phải tự đảm bảo dữ liệu được cập nhật.
Giá trị kết thúc báo rằng dữ liệu đã hết
Một máy nhận số hộp cho đến khi gặp số 0. Số 0 chỉ báo dừng, không tính là dữ liệu:
4 7 0
tong = 0
while True:
so = int(input())
if so == 0:
break
tong = tong + so
print(tong)11
while True bắt đầu mỗi lượt bằng điều kiện đúng. Giá trị kết thúc (sentinel) là giá trị đặc biệt, ở đây là 0. Khi đọc được 0, break đưa chương trình ra khỏi vòng lặp. Nếu 0 xuất hiện ngay dòng đầu, tổng vẫn là 0.
Phải đọc một giá trị mới ở mỗi lượt. Chương trình cần dữ liệu thực sự chứa giá trị kết thúc; nếu hết dòng trước đó, input() sẽ báo lỗi. Điều kiện dừng phải được kiểm tra trước khi xử lý số báo dừng.
Ví dụ khác đếm số dương và bỏ các số âm trước khi gặp 0:
-2 4 8 0
dem = 0
while True:
so = int(input())
if so == 0:
break
if so < 0:
continue
dem = dem + 1
print(dem)2
Mỗi lần quay lại đầu vòng lặp, input() đọc dòng mới. Vì vậy số âm được bỏ qua mà không giữ vòng lặp ở cùng một giá trị.
else của vòng lặp chạy khi không gặp break
Chương trình kiểm tra các số từ 2 đến 4 để tìm số chia hết cho 5:
for so in range(2, 5):
if so % 5 == 0:
print("Tim thay")
break
else:
print("Khong tim thay")Khong tim thay
else thụt cùng mức với for, không thuộc if. Khối này chạy khi vòng lặp kết thúc bình thường mà không thực hiện break. Nếu dãy rỗng, vòng lặp cũng kết thúc bình thường và else vẫn chạy.
Quy tắc tương tự áp dụng cho while:
so = 1
while so < 3:
so = so + 1
else:
print("Da het luot")Da het luot
Ở đây điều kiện trở thành sai nên else chạy. Nếu vòng lặp thoát bằng break, else bị bỏ qua. continue chỉ kết thúc lượt hiện tại; tự nó không ngăn else chạy sau khi vòng lặp kết thúc bình thường.
Chia lấy dư giúp tách chữ số cuối
Với số nguyên không âm 472, chia lấy dư cho 10 cho chữ số cuối; chia nguyên cho 10 bỏ chữ số đó:
so = 472
cuoi = so % 10
con_lai = so // 10
print(cuoi, con_lai)2 47
cuoi là 2 và con_lai là 47. Hai phép tính đều dùng giá trị ban đầu của so. Nếu gán so = so // 10 trước khi lấy dư, chữ số 2 đã bị bỏ.
| Bước | 0 | 1 | 2 |
|---|---|---|---|
| Ban đầu | 4 | 7 | 2 |
| Còn lại | 4 | 7 |
Lặp lại thao tác để tính tổng chữ số:
so = 472
tong = 0
while so > 0:
chu_so = so % 10
tong = tong + chu_so
so = so // 10
print(tong)13
Các chữ số được lấy theo thứ tự từ phải sang trái: 2, 7, 4. Sau mỗi lượt, so ngắn đi một chữ số và cuối cùng bằng 0. Khi đầu vào bằng 0, vòng lặp không chạy; tổng khởi tạo bằng 0 vẫn đúng. Nếu cần đếm chữ số, phải xử lý riêng số 0 vì cách viết while so > 0 không có lượt nào.
Phương pháp này áp dụng trực tiếp cho số không âm. Với số âm, phép // của Python làm tròn xuống, nên cần đổi sang độ lớn không âm trước khi tách.
Thuật toán Euclid tìm ước chung lớn nhất
Hai đoạn dây dài 18 và 12 đơn vị cần được cắt thành các đoạn bằng nhau, dài nhất có thể. Độ dài cần tìm là ước chung lớn nhất (greatest common divisor): số dương lớn nhất chia hết cả hai số.
a = 18
b = 12
while b != 0:
du = a % b
a = b
b = du
print(a)6
Đây là thuật toán Euclid. Mỗi lượt thay cặp (a, b) bằng số chia cũ và số dư. Với 18 và 12, cặp lần lượt là 18 và 12, rồi 12 và 6, rồi 6 và 0. Khi b bằng 0, a là kết quả.
Lưu du trước khi đổi a, vì phép chia cần hai giá trị cũ. Nếu b bằng 0 ngay từ đầu và a dương, không có lượt nào và kết quả là a. Tránh trường hợp cả hai số bằng 0 khi đề yêu cầu ước chung lớn nhất dương.
Vì sao phép thay này giữ được kết quả? Một số chia hết cả a và b cũng chia hết số dư a % b. Ngược lại, một số chia hết cả b và số dư cũng chia hết a. Hai cặp có cùng các ước chung. Khi số dư đã bằng 0, số còn lại là ước chung lớn nhất.
a = 15
b = 0
while b != 0:
du = a % b
a = b
b = du
print(a)15
Điều kiện b != 0 ngăn phép chia dư cho 0. Hai số đầu vào của ví dụ được chọn không âm, không đồng thời bằng 0.
Thử ước để kiểm tra số nguyên tố
Số nguyên tố là số nguyên lớn hơn 1 chỉ có hai ước dương là 1 và chính nó. Với số 13, thử các ước từ 2 trở lên:
n = 13
uoc = 2
while uoc < n:
if n % uoc == 0:
break
uoc = uoc + 1
if n > 1 and uoc == n:
print("Nguyen to")
else:
print("Khong nguyen to")Nguyen to
Nếu tìm thấy ước, break dừng sớm và uoc nhỏ hơn n. Nếu không tìm thấy, uoc đạt n. Điều kiện n > 1 loại 0 và 1; cả hai không phải số nguyên tố.
Với 12, lượt thử gặp 2 và dừng ngay. Với 2, uoc < n sai từ đầu nhưng 2 vẫn là số nguyên tố. Đây là hai trường hợp cần thử khi kiểm tra chương trình.
Một số lẻ không nhất thiết là số nguyên tố. Chẳng hạn, 9 chia hết cho 3, nên việc chỉ kiểm tra tính chẵn lẻ là chưa đủ. Khi kiểm tra một chương trình, bạn có thể thử 1, 2, một hợp số chẵn và một hợp số lẻ. Những ca đó giúp phát hiện việc bỏ sót biên hoặc dừng thử quá sớm.
Lỗi thường gặp
| Lỗi | Vì sao sai | Cách sửa |
|---|---|---|
Thiếu dấu : sau while | Đầu khối chưa hợp lệ | Thêm dấu : |
| Không thụt thân vòng lặp | Python không thấy khối lệnh | Thụt các lệnh cùng khối |
| Quên thay đổi biến điều kiện | Điều kiện có thể đúng mãi | Cập nhật theo hướng đến điểm dừng |
Đặt cập nhật sau continue | Một nhánh bỏ qua cập nhật | Cập nhật trước hoặc xử lý trong nhánh |
Nhầm break với continue | Hai lệnh thay đổi luồng khác nhau | Dùng break để thoát, continue để bỏ lượt |
| Cộng cả giá trị kết thúc | Số báo dừng không phải dữ liệu | Kiểm tra nó trước khi cộng |
Đếm chữ số 0 bằng vòng so > 0 | Vòng không chạy lượt nào | Xử lý riêng đầu vào 0 |
| Chia dư khi số chia bằng 0 | Phép % không nhận số chia 0 | Kiểm tra điều kiện trước phép chia |
Tóm tắt
whilekiểm tra điều kiện trước từng lượt; điều kiện sai từ đầu cho không lượt.- Cập nhật dữ liệu để vòng lặp có đường đến điểm dừng.
breakthoát vòng gần nhất;continuebỏ phần còn lại của lượt.elsecủa vòng lặp chạy khi vòng kết thúc bình thường, kể cả không có lượt.% 10lấy chữ số cuối của số không âm;// 10bỏ chữ số đó.- Euclid dùng số dư; kiểm tra nguyên tố cần loại các số không lớn hơn 1.