Example 6 — Check a Palindrome (debugging) ตัวอย่างที่ 6 — ตรวจคำที่อ่านกลับหลังได้เหมือนเดิม (debugging)
A debugging problem: the program below should tell whether a word reads the same forwards and backwards, but it hides one bug. โจทย์ debugging: โปรแกรมด้านล่างควรบอกว่าคำอ่านจากหน้าไปหลังและหลังไปหน้าได้เหมือนกันหรือไม่ แต่มี bug ซ่อนอยู่ 1 จุด
How to work on this page: read the problem and the flowchart, then check the code against the flowchart box by box. Run the test list, find the wrong line, and fix the program in the code box yourself. Run the test list again until every test gives its expected output, and only then open the solution. To review the kinds of error, the kinds of test case and the bug-hunting tools, see 11 Testing and Debugging.
วิธีทำโจทย์หน้านี้: อ่านโจทย์และ flowchart แล้วตรวจโค้ดเทียบกับ flowchart ทีละกล่อง จากนั้นรันตามรายการทดสอบ หาบรรทัดที่ผิด และแก้โปรแกรมในกล่องโค้ดเอง รันรายการทดสอบซ้ำจนทุกข้อได้ผลตามที่ควรได้ แล้วจึงเปิดดูเฉลย ทบทวนชนิดของ error, ชนิดของ test case และเครื่องมือหา bug ได้ที่บท 11 การทดสอบและการแก้ไขโปรแกรม
Read one word that contains only lowercase English letters, then tell whether the word reads the same forwards and backwards. A word like this is called a palindrome. If it does, show <word> reads the same both ways; if it does not, show <word> does not read the same both ways. A word of one letter and an empty word (nothing typed) both count as reading the same, because they have no pair of letters that differ.
รับคำ 1 คำที่มีเฉพาะตัวพิมพ์เล็กภาษาอังกฤษ แล้วบอกว่าคำนั้นอ่านจากหน้าไปหลังและหลังไปหน้าได้เหมือนกันหรือไม่ คำแบบนี้เรียกว่า palindrome ถ้าเหมือนกันให้แสดง <คำ> reads the same both ways ถ้าไม่เหมือนให้แสดง <คำ> does not read the same both ways คำที่มีตัวอักษรตัวเดียวและคำว่าง (ไม่พิมพ์อะไรเลย) นับว่าอ่านได้เหมือนกัน เพราะไม่มีตัวอักษรคู่ไหนที่ต่างกัน
- Example:ตัวอย่าง: level → level reads the same both ways
- Example:ตัวอย่าง: robot → robot does not read the same both ways
The program works in two parts: first it builds backward, the word written from the last letter to the first, one letter at a time; then it compares the word with backward letter by letter.
โปรแกรมทำงาน 2 ช่วง ช่วงแรกสร้าง backward คือคำเดิมที่เขียนจากตัวสุดท้ายไปตัวแรก ทีละตัวอักษร ช่วงที่สองเทียบคำเดิมกับ backward ทีละตัวอักษร
- word[i] is the letter at position i counted from the left, starting at 0.
- A negative index counts from the end: word[-1] is the last letter and word[-2] the one before it.
- backward += ... adds a letter to the end of the text in backward.
- != means “is not equal to”.
- mirror holds True or False and is used as a flag: it starts as True and changes to False as soon as a pair of letters differs.
- word[i] คือตัวอักษรตำแหน่งที่ i นับจากซ้าย เริ่มที่ 0
- ดัชนีติดลบนับจากท้าย word[-1] คือตัวสุดท้าย และ word[-2] คือตัวรองสุดท้าย
- backward += ... ต่อตัวอักษรเข้าท้ายข้อความใน backward
- != แปลว่า “ไม่เท่ากับ”
- mirror เก็บค่า True หรือ False ใช้เป็นตัวแปรธง คือตั้งไว้เป็น True ก่อน แล้วเปลี่ยนเป็น False เมื่อพบตัวอักษรคู่ที่ต่างกัน
The program starts by reading a word into word. Next it creates backward = "" to collect the word written backward, sets i = 0 as the letter position and checks whether i < len(word). If it is, it adds the letter word[-i] to the end of backward, increases i by 1 and checks again. When the condition is false it leaves the loop, sets mirror = True and i = 0, and checks whether i < len(word). If it is, it checks whether word[i] != backward[i]; if that is true it sets mirror = False. Either way it increases i by 1 and goes back to check i < len(word) again. When every letter has been compared it leaves the loop and checks mirror: if it is True it shows that the word reads the same both ways, otherwise it shows that it does not, with print, and the program ends.
โปรแกรมนี้เริ่มต้นด้วยรับคำจากผู้ใช้เก็บใน word ถัดไปสร้างตัวแปร backward = "" สำหรับเก็บคำที่เขียนกลับหลัง กำหนดค่า i = 0 เป็นตัวชี้ตำแหน่งตัวอักษร แล้วตรวจสอบเงื่อนไขว่า i < len(word) หรือไม่ ถ้าใช่ให้ต่อตัวอักษร word[-i] เข้าท้าย backward แล้วเพิ่ม i ขึ้น 1 และกลับไปตรวจสอบอีกครั้ง เมื่อเงื่อนไขเป็นเท็จจึงออกจาก loop หลังจากนั้นกำหนดค่า mirror = True และ i = 0 แล้วตรวจสอบว่า i < len(word) หรือไม่ ถ้าใช่ให้ตรวจสอบเงื่อนไขว่า word[i] != backward[i] หรือไม่ ถ้าใช่ให้กำหนด mirror = False ไม่ว่าทางใดทางหนึ่ง หลังจากนั้นเพิ่ม i ขึ้น 1 แล้วกลับไปตรวจสอบ i < len(word) อีกครั้ง เมื่อเทียบครบทุกตัวอักษรจึงออกจาก loop แล้วตรวจสอบ mirror ถ้าเป็น True ให้แสดงว่าคำนี้อ่านได้เหมือนกันทั้งสองทาง ถ้าไม่ใช่ให้แสดงว่าอ่านไม่เหมือนกัน ด้วย print และจบการทำงานของโปรแกรม
Type a word: level level does not read the same both ways
Try it yourself — type a word and press Submit. It replaces the [input] box and runs the code that is in the box right now (including your fix). Press Submit with an empty box to test an empty word.
ลองป้อนค่าเอง — พิมพ์คำแล้วกดส่ง คำจะแทนที่กล่อง [input] แล้วรันโค้ดที่อยู่ในกล่องตอนนี้ (รวมถึงส่วนที่คุณแก้แล้ว) ถ้ากดส่งโดยไม่พิมพ์อะไรจะเป็นการทดสอบคำว่าง
Run each input yourself (type it into the form in card 2) and compare the result with the expected output column. The last column shows what the code with the bug gives.
รันทีละข้อด้วยตัวเอง (พิมพ์ลงในช่องของการ์ดที่ 2) แล้วเทียบผลกับคอลัมน์ผลที่ควรได้ คอลัมน์สุดท้ายคือผลจากโค้ดที่ยังมี bug
| Inputข้อมูลที่ป้อน | Kindชนิด | Expected outputผลที่ควรได้ | Output of the buggy codeผลจากโค้ดที่มี bug |
|---|---|---|---|
| level | Normal | level reads the same both ways | level does not read the same both ways ✗ |
| racecar | Normal | racecar reads the same both ways | racecar does not read the same both ways ✗ |
| noon | Normal (even number of letters)Normal (จำนวนตัวอักษรเป็นเลขคู่) | noon reads the same both ways | noon does not read the same both ways ✗ |
| robot | Normal | robot does not read the same both ways | robot does not read the same both ways ✓ |
| ab | Boundary (2 letters)Boundary (2 ตัวอักษร) | ab does not read the same both ways | ab reads the same both ways ✗ |
| x | Boundary (1 letter)Boundary (ตัวอักษรเดียว) | x reads the same both ways | x reads the same both ways ✓ |
| (empty)(ว่าง) | Boundary (no letters)Boundary (ไม่มีตัวอักษร) | reads the same both ways (starts with a space: the empty word, then the space that print puts between its values) (ขึ้นต้นด้วยช่องว่าง: คำว่าง ตามด้วยช่องว่างที่ print ใส่คั่นระหว่างค่า) | reads the same both ways ✓ |
| Anna | Error (has a capital letter)Error (มีตัวพิมพ์ใหญ่) | should be rejected, but this program does not check its input; after the fix it gives Anna does not read the same both ways ควรถูกปฏิเสธ แต่โปรแกรมนี้ไม่ได้ตรวจข้อมูลที่รับเข้ามา หลังแก้แล้วจะได้ Anna does not read the same both ways | Anna does not read the same both ways |
- Check the code against the flowchart box by box, and note anything that looks suspicious.
- Run every input in the test list and note whether the actual output matches the expected output.
- Why does robot give the right answer even though the program has a bug? And why does level, which should pass, fail, while ab, which should fail, passes?
- Which kind of error is this bug, and why?
- Find the wrong line:
- add print(backward) after the first loop, to see whether backward is already wrong when it is built, or
- add print(i, -i, word[-i], backward) inside the first loop,
- Fix the code in the box and run the test list again. Every Normal and Boundary input must give its expected output.
- Think further: Anna breaks the rule of the problem, yet the program does not reject it. Where would you add a check? (Example 2 in 11 Testing and Debugging shows one way.)
- ตรวจโค้ดเทียบกับ flowchart ทีละกล่อง แล้วจดจุดที่สงสัย
- รันทุกข้อในรายการทดสอบ แล้วจดผลที่ได้จริงว่าตรงกับผลที่ควรได้หรือไม่
- ทำไม robot ได้ผลถูกทั้งที่โปรแกรมมี bug และทำไม level ที่ควรผ่านกลับไม่ผ่าน แต่ ab ที่ไม่ควรผ่านกลับผ่าน
- bug นี้เป็น error ชนิดไหน เพราะอะไร
- หาบรรทัดที่ผิด
- ใส่ print(backward) หลัง loop แรก เพื่อดูว่า backward ผิดตั้งแต่ช่วงสร้างคำหรือไม่ หรือ
- ใส่ print(i, -i, word[-i], backward) ใน loop แรก
- แก้โค้ดในกล่อง แล้วรันรายการทดสอบซ้ำ ข้อที่เป็น Normal และ Boundary ต้องได้ผลตามตารางทุกข้อ
- ชวนคิดต่อ: Anna ผิดเงื่อนไขของโจทย์ แต่โปรแกรมไม่ปฏิเสธ ควรเพิ่มการตรวจตรงไหน (ตัวอย่างที่ 2 ในบท 11 การทดสอบและการแก้ไขโปรแกรม มีวิธีหนึ่งให้ดู)
| i | -i | word[-i] | backward |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 |
Show the fixed code ดูโค้ดที่แก้แล้ว
The bug is on the line backward += word[-i]. Because i starts at 0, -i is -0, which is the same as 0, so the first round takes the first letter instead of the last one, and backward is wrong from its very first letter. Change it to backward += word[-(i + 1)]. It is a logic error: the program runs to the end with no error message. The compare loop and if mirror are already correct; they give a wrong answer only because they receive a wrong backward from the first part — the part that shows the symptom is not always the part that holds the bug.
bug อยู่ที่บรรทัด backward += word[-i] เพราะ i เริ่มที่ 0 ทำให้ -i คือ -0 ซึ่งเท่ากับ 0 รอบแรกจึงหยิบตัวแรกแทนตัวสุดท้าย และ backward ผิดตั้งแต่ตัวอักษรแรก แก้เป็น backward += word[-(i + 1)] เป็น logic error เพราะโปรแกรมรันจบโดยไม่มีข้อความ error ส่วน loop ที่เทียบคำและ if mirror ถูกต้องอยู่แล้ว แต่ได้คำตอบผิดเพราะรับ backward ที่ผิดมาจากช่วงแรก ช่วงที่แสดงอาการจึงไม่จำเป็นต้องเป็นช่วงที่มี bug
Why some tests hide the bug: robot gives backward = "rtobo", which differs from the word anyway, so the answer is right by luck. x and the empty word have no pair of letters that can differ. ab gives backward = "ab" (first word[0], then word[-1]), which is exactly the word itself, so it wrongly passes.
ทำไม test บางข้อซ่อน bug: robot ได้ backward = "rtobo" ซึ่งต่างจากคำเดิมอยู่แล้ว คำตอบจึงถูกโดยบังเอิญ ส่วน x และคำว่างไม่มีตัวอักษรคู่ไหนที่ต่างกัน และ ab ได้ backward = "ab" (ตัวแรกคือ word[0] แล้วตามด้วย word[-1]) ซึ่งตรงกับคำเดิมพอดี จึงผ่านทั้งที่ไม่ควรผ่าน
| i | -i | word[-i] | backward |
|---|---|---|---|
| 0 | 0 | 'l' | "l" |
| 1 | -1 | 'l' | "ll" |
| 2 | -2 | 'e' | "lle" |
| 3 | -3 | 'v' | "llev" |
| 4 | -4 | 'e' | "lleve" |
| i | word[i] | backward[i] | mirror at the end of the roundmirror หลังจบรอบ |
|---|---|---|---|
| 0 | 'l' | 'l' | True |
| 1 | 'e' | 'l' | False |
| 2 | 'v' | 'e' | False |
| 3 | 'e' | 'v' | False |
| 4 | 'l' | 'e' | False |
After the fix, -(i + 1) runs through -1, -2, -3, -4, -5, so backward grows as l, le, lev, leve, level, and mirror stays True in every round.
หลังแก้แล้ว -(i + 1) มีค่า -1, -2, -3, -4, -5 ตามลำดับ backward จึงเพิ่มทีละตัวเป็น l, le, lev, leve, level และ mirror เป็น True ทุกรอบ
Type a word: level level reads the same both ways
The Run buttons on this page simulate Python with JavaScript, the same as the other lessons. To run the real Python, paste the code into Google Colab.
ปุ่ม Run ในหน้านี้จำลองโค้ด Python ด้วย JavaScript เหมือนบทเรียนอื่นๆ หากต้องการรัน Python จริง ให้คัดลอกโค้ดไปวางที่ Google Colab
References References / แหล่งอ้างอิง
- Python docs — Common Sequence Operations (indexing a string, including negative indexes counted from the end) (การใช้ดัชนีกับสตริง รวมถึงดัชนีติดลบที่นับจากท้าย)
-
Python docs — Comparisons
(
!=, “not equal”) (!=“ไม่เท่ากับ”) -
Python docs — Boolean Type
(
TrueandFalse) (TrueและFalse) - Kinds of error, kinds of test case and bug-hunting tools: 11 Testing and Debugging; flowchart symbols: 01 Basic → Flowchart Basic. ชนิดของ error, ชนิดของ test case และเครื่องมือหา bug: 11 การทดสอบและการแก้ไขโปรแกรม ; สัญลักษณ์ของ flowchart: 01 Basic → Flowchart Basic