Trong bài này
Lan chuẩn bị 4 túi quà cho ngày hội đọc sách. Mỗi túi cần một nhãn có số thứ tự, nhưng Lan không muốn viết lại cùng một lệnh bốn lần. Vòng lặp giúp Lan mô tả việc cần làm và để Python thực hiện theo từng lượt.
for thực hiện một khối lệnh theo từng lượt
Hãy chạy chương trình đánh số ba kệ sách:
for so in range(3):
print(so)0 1 2
range(3) tạo dãy số nguyên 0, 1, 2 để duyệt. Vòng lặp (loop) for lần lượt gán mỗi số cho so, rồi thực hiện khối lệnh thụt vào. Mỗi lần thực hiện khối đó là một lượt lặp (iteration).
Dấu : kết thúc dòng mở đầu vòng lặp. Bốn dấu cách trước print(so) cho biết lệnh nằm trong khối lặp. Với range(n) và n không âm, có n lượt; số cuối là n - 1 nếu n lớn hơn 0.
| 0 | 1 | 2 |
|---|---|---|
| 0 | 1 | 2 |
Sau mỗi lượt, Python quay lại lấy giá trị kế tiếp. Khi không còn giá trị, Python chạy tiếp dòng nằm ngoài khối:
for so in range(2):
print("Ke", so)
print("Xong")Ke 0 Ke 1 Xong
Xong chỉ được in một lần vì dòng đó không thụt vào. Dòng in tên kệ chạy hai lần. Bạn có thể đặt nhiều lệnh cùng mức thụt vào để thực hiện cả hai ở mỗi lượt:
for so in range(2):
print("Bat dau", so)
print("Da kiem tra")Bat dau 0 Da kiem tra Bat dau 1 Da kiem tra
Hai lệnh trong khối thực hiện theo thứ tự từ trên xuống ở từng lượt. Python in dòng bắt đầu và dòng kiểm tra cho kệ 0 trước. Sau đó Python mới chuyển sang kệ 1 và thực hiện lại cả hai lệnh. Vì vậy, số dòng in có thể lớn hơn số lượt của vòng lặp.
Với range(0), không có giá trị để duyệt nên khối lặp không chạy. Lệnh ngoài khối vẫn chạy:
for so in range(0):
print(so)
print("Hoan tat")Hoan tat
Nếu quên thụt lề sau dấu :, Python không nhận được khối lệnh:
for so in range(2):
print(so)Lỗi khi chạy, ở dòng 2
IndentationError: expected an indented block after 'for' statement on line 1
Hãy thụt dòng print vào. Nếu quên dấu :, Python cũng báo lỗi cú pháp; thêm dấu đó sau range(2).
range() chọn điểm đầu, điểm dừng và bước nhảy
Để đánh số kệ từ 2 đến 5, bạn chọn điểm đầu là 2 và điểm dừng là 6:
for so in range(2, 6):
print(so)2 3 4 5
range(a, b) bắt đầu tại a, tăng từng đơn vị và dừng trước b. Số 6 không được duyệt. Để lấy cả 6, dùng điểm dừng 7. Khi điểm đầu bằng điểm dừng, dãy rỗng:
for so in range(4, 4):
print(so)
print("Het")Het
Tham số thứ ba chọn bước nhảy (step). Ví dụ sau đánh dấu các vị trí chẵn từ 2 đến 8:
for so in range(2, 10, 2):
print(so)2 4 6 8
Python bắt đầu ở 2, cộng 2 sau mỗi lượt và dừng trước 10. Bước nhảy không bắt buộc phải chia hết khoảng cách giữa hai mốc:
for so in range(1, 9, 3):
print(so)1 4 7
Các giá trị là 1, 4, 7. Giá trị tiếp theo là 10, đã vượt điểm dừng nên không được duyệt. Với bước dương, điểm đầu lớn hơn điểm dừng tạo dãy rỗng.
Bạn có thể chọn các mốc bằng biến nguyên đã nhập hoặc đã tính. Python xác định khoảng cần duyệt khi bắt đầu vòng lặp. Hãy kiểm tra riêng điểm đầu, hướng bước và điểm dừng khi tự dự đoán các lượt. Cách này giúp bạn không nhầm số lượt với giá trị lớn nhất.
Để đếm ngược, dùng bước âm và đặt điểm dừng nhỏ hơn điểm đầu:
for so in range(5, 1, -1):
print(so)5 4 3 2
Dãy gồm 5, 4, 3, 2; số 1 vẫn là điểm dừng không được lấy. Muốn in cả 1, đặt điểm dừng là 0. Nếu bước âm nhưng điểm đầu nhỏ hơn điểm dừng, dãy rỗng.
for so in range(1, 5, -1):
print(so)
print("Khong co so")Khong co so
Bước bằng 0 không hợp lệ vì không thể tiến đến điểm dừng:
for so in range(1, 5, 0):
print(so)Lỗi khi chạy, ở dòng 1
ValueError: range() arg 3 must not be zero
Chọn bước khác 0, đúng hướng cần duyệt. Các tham số của range() phải là số nguyên; nếu đọc từ bàn phím, hãy đổi dữ liệu bằng int() trước.
Biến tích lũy lưu kết quả sau mỗi lượt
Lan muốn biết tổng số trang đọc trong ba ngày. Ngày thứ k, Lan đọc k trang. Ví dụ sau cộng số trang của ba ngày đó:
tong = 0
for trang in range(1, 4):
tong = tong + trang
print(trang, tong)
print("Tong", tong)1 1 2 3 3 6 Tong 6
tong bắt đầu bằng 0 trước vòng lặp. Mỗi lượt lấy giá trị cũ của tong, cộng trang, rồi lưu lại vào chính tong. Đây là biến tích lũy (accumulator). Lệnh cuối nằm ngoài vòng lặp nên chỉ in tổng sau khi đã cộng xong.
Ở lượt đầu, phép cộng dùng tổng cũ là 0. Lượt sau dùng kết quả của lượt trước, không trở lại 0. Dấu = trong tong = tong + trang là phép gán đã học, không phải khẳng định hai vế luôn bằng nhau. Python tính vế phải trước rồi cập nhật biến ở vế trái.
| Trang | Tổng sau khi cộng |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
Nếu đặt tong = 0 bên trong vòng lặp, mỗi lượt sẽ xóa kết quả đã cộng trước đó. Chạy thử để thấy sự khác biệt:
for trang in range(1, 4):
tong = 0
tong = tong + trang
print(tong)3
Kết quả chỉ còn giá trị của lượt cuối. Khởi tạo trước vòng lặp để giữ kết quả qua các lượt. Với dãy rỗng, tổng vẫn là 0 vì không có lượt nào thay đổi nó.
Để đếm, chỉ tăng biến khi điều kiện thỏa mãn. Ví dụ sau đếm các số chia hết cho 3 từ 1 đến 8:
dem = 0
for so in range(1, 9):
if so % 3 == 0:
dem = dem + 1
print(dem)2
if nằm trong vòng lặp; lệnh tăng dem nằm trong if. Hai số 3 và 6 thỏa điều kiện, nên kết quả là 2. Nếu đặt lệnh tăng cùng mức với if, mọi lượt đều được đếm.
Tính tích cũng lưu kết quả qua từng lượt, nhưng bắt đầu từ 1:
tich = 1
for so in range(1, 5):
tich = tich * so
print(tich)24
Bắt đầu bằng 0 sẽ khiến mọi tích sau đó vẫn bằng 0. Khi không có lượt nhân nào, giá trị ban đầu 1 được giữ lại. Bạn cần xác định giá trị khởi đầu theo phép tính và yêu cầu bài toán.
Các lệnh cập nhật như tong = tong + trang rất hay gặp, nên Python có cách viết gọn. Phép gán tăng cường (augmented assignment) tong += trang có cùng tác dụng với tong = tong + trang. Tương tự, có -=, *=, //= và %=:
dem = 0
tich = 1
for so in range(1, 5):
dem += 1
tich *= so
print(dem, tich)4 24
Biến phải có giá trị trước khi dùng +=, vì Python vẫn đọc giá trị cũ để tính. Từ đây, các ví dụ có thể dùng cả hai cách viết.
Vòng lặp lồng nhau thực hiện từng hàng và từng cột
Một bảng có hai hàng, mỗi hàng ba ô. Vòng ngoài chọn hàng; với mỗi hàng, vòng trong đi qua các cột:
for hang in range(1, 3):
for cot in range(1, 4):
print(hang, cot)1 1 1 2 1 3 2 1 2 2 2 3
Đây là vòng lặp lồng nhau (nested loop). Ở hàng 1, vòng trong chạy đủ ba lượt. Sau đó hàng 2 bắt đầu và vòng trong lại chạy đủ ba lượt. Tổng cộng có 2 × 3 = 6 lần in.
Hàng 1Duyệt cột 1, 2, 3Hàng 2Duyệt cột 1, 2, 3Kết thúcĐã duyệt sáu ô
Bạn có thể in hình chữ nhật bằng dấu sao. end="" giữ các dấu sao trên cùng dòng; print() sau vòng trong chuyển sang hàng tiếp theo:
for hang in range(2):
for cot in range(3):
print("*", end="")
print()*** ***
print() cuối thụt cùng mức với vòng trong, nên chạy một lần mỗi hàng. Nếu đưa nó vào vòng trong, mỗi dấu sao sẽ nằm trên một dòng riêng. Nếu đặt nó ngoài cả hai vòng, các hàng không tách dòng.
Số lượt của vòng trong có thể phụ thuộc vào hàng. Hình sau tăng từ một đến ba dấu sao:
for hang in range(1, 4):
for cot in range(hang):
print("*", end="")
print()* ** ***
Ở hàng 2, range(hang) có hai lượt. Khi hang đổi, vòng trong dùng giá trị mới để tạo hàng kế tiếp. Không dùng cùng một tên biến cho cả hai vòng nếu cần phân biệt hàng và cột.
Bảng phép nhân có thể in từng phép tính trên một dòng:
for bang in range(2, 4):
for so in range(1, 4):
print(bang, "x", so, "=", bang * so)2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
Vòng ngoài chọn bảng 2 rồi bảng 3. Với mỗi bảng, vòng trong lần lượt chọn 1, 2, 3. Hãy đọc yêu cầu để chọn số hàng, số cột và vị trí xuống dòng.
Với hình chữ nhật có số cột cố định, số lần in ô bằng số hàng nhân số cột. Nếu một trong hai vòng không có lượt, không có ô nào được in. Với hình bậc thang, số lượt vòng trong thay đổi theo hàng nên phải cộng số ô của từng hàng. Không thể lấy số hàng nhân số cột cố định trong trường hợp đó.
for đọc số dòng đã biết và xử lý từng dòng
Một lớp ghi số sách mượn trong từng ngày. Dòng đầu cho biết có bao nhiêu ngày; mỗi dòng sau chứa một số nguyên:
3 4 2 5
n = int(input())
tong = 0
for _ in range(n):
so_sach = int(input())
tong = tong + so_sach
print(tong)11
n bằng 3 nên vòng lặp đọc đúng ba dòng tiếp theo. Dấu _ ở đây là tên biến hợp lệ, được dùng theo quy ước khi không cần giá trị lượt lặp. Mỗi số mới được cộng vào tong; chương trình không cần giữ lại từng số.
Thứ tự đọc phải khớp với thứ tự đề bài cho. Dòng số lượng được đọc trước để biết phải lặp bao nhiêu lần. Biến so_sach thay bằng giá trị mới ở lượt kế tiếp, nhưng tổng đã giữ lại đóng góp của giá trị trước. Nhờ đó, bạn xử lý được dữ liệu ngay khi đọc.
Nếu cần thứ tự của dòng, dùng một tên rõ nghĩa thay cho _:
2 7 4
n = int(input())
for thu_tu in range(1, n + 1):
so_sach = int(input())
print(thu_tu, so_sach)1 7 2 4
n + 1 là điểm dừng không được lấy, vì thế lượt cuối có số thứ tự n. Mỗi lần gọi input() đọc dòng tiếp theo. Không đặt input() trước vòng lặp nếu muốn đọc một số mới ở mỗi lượt.
Bạn có thể kết hợp đọc và điều kiện để đếm các ngày đạt mục tiêu. Nếu n bằng 0, dữ liệu không có dòng số sách nào và kết quả là 0:
4 5 2 8 0
n = int(input())
dem = 0
for _ in range(n):
so_sach = int(input())
if so_sach >= 5:
dem = dem + 1
print(dem)2
Khi đề hứa có đúng n dòng, đừng đọc thêm một dòng sau vòng lặp. Dòng đầu chứa n không phải là một số sách cần cộng. Đọc quá số dòng được cung cấp gây lỗi:
2 4 5
n = int(input())
for _ in range(n + 1):
so_sach = int(input())Lỗi khi chạy, ở dòng 3
EOFError: EOF when reading a line
Hãy dùng range(n) để đọc đúng số dòng. Với mỗi đề, kiểm tra dữ liệu có cho phép n = 0 hay không và chọn giá trị ban đầu phù hợp.
Lỗi thường gặp
| Lỗi | Vì sao sai | Cách sửa |
|---|---|---|
Thiếu : sau dòng for | Đầu khối chưa đúng cú pháp | Thêm : trước khối lệnh |
| Không thụt lệnh trong vòng lặp | Python không thấy khối lệnh | Thụt các lệnh của khối vào cùng mức |
| Nghĩ điểm dừng được lấy | range() loại điểm dừng | Tăng điểm dừng một đơn vị khi cần lấy mốc nguyên cuối |
| Chọn bước âm để đi lên | Hướng bước không đến khoảng cần duyệt | Đổi dấu bước hoặc đổi thứ tự hai mốc |
| Đặt tổng về 0 mỗi lượt | Kết quả trước bị xóa | Khởi tạo trước vòng lặp |
| Đặt tích ban đầu bằng 0 | Mọi phép nhân tiếp theo giữ tích bằng 0 | Khởi tạo bằng 1 khi tính tích |
Đọc n + 1 dòng | Có thể đọc quá dữ liệu | Dùng đúng số lượt đề yêu cầu |
| Xuống dòng trong vòng cột | Mỗi ô bị in trên một dòng | Chuyển print() ra sau vòng trong |
Tóm tắt
forgán từng giá trị và chạy khối thụt lề một lượt cho mỗi giá trị.range()lấy điểm đầu, không lấy điểm dừng; bước phải khác 0 và đúng hướng.- Khởi tạo tổng, số đếm bằng 0 và tích bằng 1 trước khi cập nhật.
- Vòng trong chạy hết cho mỗi lượt của vòng ngoài.
- Dùng
for _ in range(n)để đọc và xử lý đúngndòng.