Nhập môn Tư duy Lập trình/Sửa bằng giả thuyết, không sửa mò
28/31
Bài 28 / 31~12 phútKhi code saiMiễn phí lượt xem

Sửa bằng giả thuyết, không sửa mò

Đổi một dấu, chạy lại, vẫn sai, đổi tiếp — sửa mò không hội tụ vì không lượt nào loại bớt được nguyên nhân. Vòng đoán rồi kiểm chứng thì có.

TL;DR: Đổi một dấu, chạy lại, vẫn sai, đổi tiếp — sửa mò không hội tụ vì không lượt nào loại bớt được nguyên nhân. Bài này thay bằng vòng bốn bước: đặt giả thuyết kiểm chứng được, chọn phép kiểm rẻ nhất có thể bác bỏ nó, chạy rồi đọc kết quả, rồi thu hẹp nghi ngờ dù giả thuyết đúng hay sai. Hai dụng cụ kiểm chứng là print và bảng trace, chọn theo việc cần soi một giá trị hay nhiều biến qua nhiều vòng lặp. Ca mẫu là một lỗi logic thật: đặt tong = 0 nhầm vào bên trong thân vòng lặp, khiến chương trình chạy trót lọt nhưng in ra đúng phần tử cuối.

Bạn viết xong một đoạn code, chạy, sai. Đổi dấu > thành >=, chạy lại, vẫn sai. Đổi tên biến, chạy lại, vẫn sai. Đổi ngược lại đúng dòng vừa sửa, chạy lại — bỗng dưng đúng, và bạn không biết vì sao nó đúng.

Đây gọi là sửa mò, và nó không hội tụ. Giả sử đoạn code mười dòng của bạn có năm chỗ khả nghi. Mỗi lượt sửa mò đổi đúng một chỗ, chạy lại, thấy vẫn sai — nhưng bạn KHÔNG loại được chỗ nào trong bốn chỗ còn lại, vì chưa hề kiểm tra riêng chỗ đó có phải thủ phạm hay không. Sau mười lượt, vùng nghi ngờ vẫn là năm chỗ y hệt lúc đầu — bạn chỉ đang đoán, không đang thu hẹp.

Có một cách khác: đoán CÓ CHỦ ĐÍCH rồi kiểm tra đúng đoán đó — dù đúng dù sai đều biết thêm một điều mới. Đó là nội dung bài này.

Bảng trace ở bài đó là công cụ để đóng vai máy, đọc hiểu một đoạn code chạy đúng. Ở bài này, nó đổi vai: từ công cụ để HIỂU code thành công cụ để KIỂM CHỨNG một đoán về code đang chạy sai.

Năm ca biên ở bài đó — rỗng, một phần tử, số 0, số âm, trùng nhau — chính là nguồn giả thuyết có sẵn: nghi code sai, việc đầu tiên đáng thử là những ca đó, thay vì đoán ngẫu nhiên.

Bài này lo đúng một việc: biến print và bảng trace thành công cụ kiểm chứng giả thuyết, thay vì chỉ để đọc hiểu code.

1. Analogy — bóng đèn không sáng

Đèn phòng khách nhà bạn không sáng. Bạn không tháo tung cả hệ thống điện (ổ cắm, dây dẫn, cầu dao, bóng đèn) ra kiểm tra cùng lúc. Bạn đoán CÓ CHỦ ĐÍCH: "chắc là bóng đèn cháy", rồi kiểm chứng bằng cách rẻ nhất: thay một bóng đã biết còn tốt vào đúng ổ đó. Bật công tắc, nhìn kết quả. Sáng: đúng là bóng cháy. Không sáng: loại bóng đèn, chuyển sang giả thuyết kế tiếp, có khi là cầu dao.

Đèn không sángCode chạy sai
Giả thuyết: "chắc là bóng đèn cháy"Giả thuyết: "tong bị đặt lại về 0 ở mỗi vòng lặp"
Kiểm chứng rẻ nhất: thay một bóng đã biết còn tốt vào đúng ổ đóKiểm chứng rẻ nhất: in giá trị tong ngay trong thân vòng lặp
Bật công tắc, nhìn kết quảChạy chương trình, đọc output
Sáng: đúng là bóng cháy. Không sáng: loại bóng đèn, chuyển giả thuyết kháctong không tăng dần qua các dòng in: giả thuyết đúng. tong tăng dần bình thường: loại giả thuyết, chuyển hướng khác
💡 Cách nhớ

Bạn không tháo tung cả hệ thống điện chỉ vì một bóng không sáng. Bạn thử đúng chỗ nghi ngờ nhất trước — dù kết quả thế nào, bạn cũng biết thêm một điều mà lúc trước chưa biết.

2. Vòng bốn bước

Vòng bốn bước thay sửa mò bằng bốn thao tác lặp lại tới khi tìm ra nguyên nhân thật:

  1. Giả thuyết kiểm chứng được. "Tôi nghĩ tong bị đặt lại về 0 ở mỗi vòng lặp" có một quan sát cụ thể sẽ xác nhận hoặc bác bỏ nó. "Chắc vòng lặp có vấn đề" thì không — không quan sát nào bác bỏ được một câu mơ hồ.
  2. Phép kiểm chứng rẻ nhất bác bỏ được nó. Không cần đọc lại toàn bộ hàm; chỉ cần một dòng print đúng chỗ, hoặc một hàng bảng trace tại điểm nghi ngờ.
  3. Chạy, đọc kết quả. Đọc đúng con số hiện ra, không đọc theo điều bạn MUỐN thấy.
  4. Thu hẹp — dù đúng dù sai. Đúng: tìm ra thủ phạm, sửa xong. Sai: loại được một chỗ khỏi danh sách nghi ngờ, còn ít chỗ hơn để thử tiếp. Đây là thứ sửa mò không có.

So với năm chỗ nghi ngờ ở đầu bài, mỗi vòng loại bớt một chỗ — sau năm lượt, không phải mười, còn lại đúng một thủ phạm.

Bốn hộp xếp theo hình chữ Z: giả thuyết kiểm chứng được, phép kiểm chứng rẻ nhất, chạy và đọc kết quả, rồi tới hộp viền xanh jade đậm "vùng nghi ngờ hẹp lại". Một hộp xanh lá bên dưới ghi "dù giả thuyết đúng hay sai, vùng nghi ngờ đều hẹp lại", nối bằng một đường vòng chạy qua hành lang bên phải quay ngược lên hộp đầu tiên với nhãn "chưa ra thủ phạm thì lặp lại" — thể hiện đây là một vòng lặp liên tục, không phải bốn việc làm một lần rồi thôi.

3. Print hay bảng trace — chọn thế nào?

print rẻ và nhanh: thêm một dòng, chạy lại, đọc giá trị TẠI đúng một điểm nghi ngờ. Dùng khi giả thuyết chỉ nhắm một biến ở một chỗ cụ thể.

Bảng trace tốn công hơn nhưng cho một bức tranh rộng hơn: nhiều biến, qua nhiều vòng lặp, trên cùng một bảng. Dùng khi giả thuyết liên quan tới CÁCH một giá trị biến đổi qua thời gian — như "tong có cộng dồn qua từng vòng hay bị đặt lại?" — một print tại một điểm không đủ trả lời câu đó.

4. Thử đoán — tổng điểm một lớp bốn bạn

Chương trình sau tính tổng điểm của bốn bạn trong danh sách diem:

diem = [8, 5, 9, 6]

for d in diem:
    tong = 0
    tong = tong + d

print(tong)
🖊️ Thử đoán

Đừng chạy vội. Nhìn kỹ bốn dòng thân vòng lặp, đoán xem dòng print(tong) cuối cùng in ra số nào. Viết ra con số bạn tin chắc trước khi đọc tiếp.

Kết quả

Giờ tự chạy vòng bốn bước cho đúng chương trình này — làm từng bước một, đối chiếu xong mới sang bước kế.

🖊️ Bước 1

Vì sao tong ra đúng bằng phần tử cuối, không phải tổng bốn số? Tự viết một giả thuyết kiểm chứng được trước khi mở đối chiếu.

Bước 1 — đối chiếu
🖊️ Bước 2

Phép kiểm chứng rẻ nhất cho giả thuyết đó là gì, đặt ở dòng nào?

Bước 2 — đối chiếu
🖊️ Bước 3

Đoán bốn dòng print(tong) bên trong vòng lặp sẽ in ra số nào.

Bước 3 — đối chiếu
🖊️ Bước 4

So bốn dòng vừa in với bốn phần tử của diem: giả thuyết Bước 1 đúng hay sai? Sửa dòng nào?

Bước 4 — đối chiếu

5. Tới lượt bạn

Cùng vòng bốn bước, áp cho một bug khác: đếm xem có bao nhiêu bạn đạt điểm từ 5 trở lên trong danh sách diem.

🐛 Tìm bug trong code này
diem = [4, 7, 9, 3]
dem_dat = 0

for d in diem:
  if d >= 5:
      dem_dat = 1

print(dem_dat)

6. Bẫy thường gặp

Nhầm 1 — đổi một chỗ rồi chạy lại, không kèm giả thuyết kiểm chứng được. Thấy vẫn sai thì đổi tiếp, hết sai thì dừng mà không biết vì sao — đúng cảnh mở bài.

✅ Trước khi đổi bất kỳ dòng nào, viết thành một câu: "tôi nghĩ biến X sai vì lý do Y" — không viết được câu đó thì chưa nên sửa.

Nhầm 2 — đọc kết quả kiểm chứng theo điều mình muốn thấy. Thấy print(tong) in ra 8, 5, 9, 6 mà vẫn tự nhủ "chắc lỗi ở chỗ khác" — bỏ qua bằng chứng trước mắt.

✅ Đọc đúng con số hiện ra trước, so với điều giả thuyết dự đoán, rồi mới kết luận.

Nhầm 3 — chọn print khi cần thấy cả một quá trình qua nhiều vòng. print(tong) đặt SAU vòng lặp chỉ cho một con số cuối, không phân biệt "không cộng dồn" với "cộng dồn đúng nhưng tình cờ ra cùng số".

✅ Nghi biến đổi qua nhiều vòng thì đặt print BÊN TRONG vòng lặp, hoặc dùng bảng trace.

7. 📚 Đào sâu — vì sao vòng bốn bước giống phương pháp khoa học

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

Vòng bốn bước là dạng thu nhỏ của phương pháp khoa học: đặt giả thuyết, tìm phép thử có thể BÁC BỎ nó, rồi kết luận theo kết quả thử — không theo niềm tin ban đầu.

Chữ đáng nhớ nhất là "bác bỏ được": "chắc vòng lặp có vấn đề" không phép thử nào bác bỏ được, còn "tong bị đặt lại mỗi vòng" thì một dòng print bác bỏ hoặc xác nhận ngay — đó là lý do sửa mò không tiến bộ được lượt nào.

8. Liên hệ các bài khác

9. Tóm tắt

  • Sửa mò không hội tụ vì không lượt nào loại được nguyên nhân; vòng bốn bước loại được ít nhất một chỗ mỗi lần, kể cả khi giả thuyết sai.
  • Câu "chắc vòng lặp có vấn đề" không trỏ tới một lệnh print nào để mà kiểm, nên nó để bạn đứng nguyên chỗ cũ.
  • print soi đúng một điểm; bảng trace soi cả một quá trình qua nhiều vòng lặp — chọn công cụ theo câu hỏi đang cần trả lời, không theo thói quen.
  • tong = 0 đặt sai chỗ, bên trong thân vòng lặp thay vì trước nó, là lỗi logic: không traceback, chạy trót lọt, chỉ lộ ra khi bạn tự đặt đúng câu hỏi.
  • Đếm số bạn đạt điểm ở mục 5 gãy vì cùng kiểu lỗi gán-lại-thay-vì-cộng-dồn — vòng bốn bước dùng lại được nguyên vẹn cho cả hai bug khác nhau.

10. Tự kiểm tra

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Vì sao sửa mò — đổi một chỗ, chạy lại, thấy vẫn sai — không hội tụ dù thử bao nhiêu lần?
  2. Q2
    Câu "tôi nghĩ tong bị đặt lại về 0 ở mỗi vòng lặp" và câu "chắc vòng lặp có vấn đề" khác nhau ở điểm nào khiến một câu kiểm chứng được, câu kia không?
  3. Q3
    Ở ca mẫu tính tổng bốn bạn, vì sao đặt print(tong) BÊN TRONG thân vòng lặp mới kiểm chứng được giả thuyết, còn đặt SAU vòng lặp như code gốc thì không?
  4. Q4
    Khi nào nên chọn bảng trace thay vì print để kiểm chứng một giả thuyết?
  5. Q5
    Giả sử giả thuyết ban đầu của bạn SAI — kiểm chứng cho thấy tong vẫn cộng dồn đúng. Việc đó có phải một bước lùi không? Vì sao?
  6. Q6
    Vì sao lỗi tong = 0 đặt trong thân vòng lặp không có traceback nào để đọc, trong khi ca diem[3] ở bài trước lại có?

Bài tiếp theo: Kẹt là bình thường — quy tắc hai mươi phút

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

Kẹt là bình thường — quy tắc hai mươi phút