Programmer Diary

ไดอารี่โปรแกรมเมอร์

Oct 2009 29 10:57 am

ผิดแบบนี้มันน่า… ใน Java ตอนที่ 1

Share/Save/Bookmark

สำหรับคนที่เป็นโปรแกรมเมอร์ คงจะมีหลายๆ ครั้งที่โปรแกรมทำงานได้ไม่ถูกต้องอย่างที่ต้องการ ต้องนั่งไล่โค้ดของตัวเอง แล้วสุดท้ายมาเจอว่าตัวเองผิดเองแบบไม่น่าให้อภัย อยากจะเอาหัวโขกกำแพง (หรืออาจจะอยากด่าตัวเองว่า ผิดแบบโง่ๆ) ซึ่งบางครั้งทำเอาเสียเวลาไปไม่น้อยเหมือนกัน (รวมถึงเสียหน้า ถ้าไปตามคนอื่นมาช่วยดู)

บางครั้ง ความผิดพลาดแบบนี้เกิดจากความเผลอเรอ ไม่ได้ตั้งสติให้ดีตอนที่เขียน รวมทั้งตอนที่ไล่โค้ด ก็ไม่ได้สังเกตเห็นข้อผิดพลาดนั้น ทั้งที่บางครั้งมันชัดเจนจนจะทิ่มลูกกะตาอยู่แล้ว ส่วนใหญ่ต้องใช้วิธีหยุดพัก ไปตั้งสติดีๆ แล้วกลับมาดูใหม่ ถึงจะพบว่าผิดตรงไหน

แต่บางครั้ง ความผิดพลาดเหล่านี้อาจเกิดจากการที่คนเขียนโค้ดขาดความรู้ ความเข้าใจ ไม่ว่าจะเป็นเรื่องพื้นฐาน ตรรกะ หรือแม้แต่โครงสร้างและความสามารถของภาษาโปรแกรมมิ่งที่ใช้ กรณีนี้ มักจะหายังไงก็หาไม่เจอว่าผิดตรงไหน ต้องหาคนมาช่วยดูให้

วันนี้ ถือโอกาส เปิด section ใหม่ “Programmer Diary” ขอเริ่มด้วยการรวบรวมข้อผิดพลาดที่เห็นแล้วมันน่า… (เติมคำในช่องว่างตามจินตนาการ) ในการเขียนภาษา Java ที่ผมเคยพบมา (ทั้งด้วยตัวเองและจากโค้ดของคนอื่น)
ลองมาดูกันว่า คุณเคยผิดอย่างนี้กันไหม?

ลูปไม่รู้จบ

เมื่อประมาณ 10 ปีที่แล้ว (นานขนาดนั้นยังจำได้อีกนะเนี่ย) เย็นวันหนึ่งใกล้เลิกงาน ผมได้รับโทรศัพท์จากเพื่อนสมัยเรียนซึ่งกำลังหัดเขียน Java ว่า “เฮ้ย ติด infiinite loop ว่ะ”

หลังจากฟังคำอธิบายโค้ดทางโทรศัพท์แล้ว ก็ยังนึกไม่ออกว่าลูปในการหาค่า sum ธรรมดาๆ แบบนั้น จะกลายเป็นลูปไม่รู้จบได้ยังไง เลยบอกให้ส่งโค้ดมาให้ดูทางอีเมลละกัน ตัวโค้ดจริงๆ เป็นยังไงผมจำไม่ได้แล้ว แต่มีลักษณะคล้ายๆ กับโค้ดตัวอย่างนี้

int[] numbers = ...;

int i = 0;
int sum = 0;
while (i < numbers.length) {
  sum = sum + numbers[i];
  i = i++;
}
System.out.println(sum);

พอได้เห็นโค้ด ขำแทบตกเก้าอี้ ไม่คิดเลยว่าจะตายน้ำตื้นขนาดนี้
เจอหรือยังครับว่าผิดตรงไหน ถ้ายังไม่เจอ อ่านต่อไปก่อน

ใน Java นั้นมีวิธีการเขียนโค้ดเพื่อ increment ค่าตัวเลขอยู่ด้วยกันหลายวิธี วิธีที่ใช้กันทั่วไป มีดังนี้

i = i + 1;
i += 1;
i++;
++i;

จากตัวอย่างโค้ดด้านบน จะเห็นว่า การใช้ i = i++ นั้นไม่อยู่ในรูปแบบของตัวอย่างการ increment ที่ผมเขียนไว้ คำถามคือ แล้ว โค้ดแบบนั้นทำงานได้ถูกต้องหรือไม่

คำตอบคือ ไม่ เพราะว่า การวาง ++ ไว้หลังตัวแปร คือการนำค่าไปใช้ก่อน แล้วค่อย increment ยิ่งในกรณีนี้เป็นการ assign ค่ากลับเข้าไปหาตัวแปรเดิมอีก ยิ่งทำให้งงไปกันใหญ่ ว่าตกลงแล้วจะ assign ค่าให้ i ด้านซ้ายมือก่อน หรือ จะ increment ค่า i ก่อน ถ้าเราใช้ System.out.println() เพื่อพิมพ์ค่า i ออกมาดูหลังจาก i = i++ แล้ว จะเห็นได้เลยว่า i ไม่มีการเปลี่ยนแปลงค่าเลย ถ้าเขียนเป็นขั้นตอนการทำงานของ i = i++ จะได้ดังนี้

  1. การประมวลผล i = i++ จะเริ่มต้นด้วยการประมวลผลของ i++ ก่อน โดยขั้นแรก ค่าของ i เดิม (ซึ่งเป็น 0) จะถูกนำไปแทนที่ i++ ดังนั้น i = i++ จะกลายเป็น i = 0
  2. หลังจากที่นำค่าเดิมไปใช้แล้ว i จะถูก increment เป็น 1 ถึงตรงนี้คือ จบขั้นตอนของ i++
  3. i = 0 ที่ถูกทดไว้ในขั้นตอนแรก จะถูกนำมาประมวลผล ทำให้ค่า i ที่เป็น 1 ในขั้นตอนที่แล้ว กลับเป็น 0 เหมือนเดิม

ความผิดพลาดนี้ เกิดจากที่คนเขียนโค้ดไม่แม่นในการใช้โอเปอเรเตอร์ ++ และยังไม่ค่อยมีประสบการณ์ในการเขียน Java (และภาษาในตระกูล C) เลยพยายามเขียน increment ด้วยวิธีที่ซับซ้อนเกินควร ซึ่งทำให้ทำงานไม่ถูกต้องด้วย

อีกประเด็นหนึ่งที่น่าสนใจคือ ในกรณีที่มีการวนลูปด้วยจำนวนครั้งที่แน่นอนแบบนี้ ควรจะใช้ for ดีกว่า เพราะอ่านเข้าใจได้ง่ายกว่า ว่า วนลูปตั้งแต่ค่าไหนไปค่าไหน เป็นจำนวนกี่ครั้ง ใช้อะไรเป็น counter และช่วยให้ไม่ลืมที่จะ increment ค่า counter ด้วย ลองนึกภาพว่าถ้าโค้ดในลูปมีความยาวมากกว่านี้ ถ้าต้องมานั่งไล่หาว่า counter อยู่ตรงไหน คงไม่สนุกแน่

int sum = 0;
for (int i = 0; i < numbers.length; i++) {
  sum = sum + numbers[i];
}
System.out.println(sum);

ฝากไปคิดเป็นการบ้านนะครับ ว่า i = ++i สามารถใช้ได้หรือเปล่า เพราะอะไร

ก่อนจะจบตอนนี้ ฝากตัวอย่างของตอนหน้าไว้ให้ดูก่อนครับ

PrintWriter out = null;
try {
  out = new PrintWriter(new FileWriter("D:\test\test.txt"));
} catch (IOException e) {}

out.println("Hello World");
out.close();

เคยผิดแบบนี้กันบ้างหรือเปล่าครับ??

3 Comments to “ผิดแบบนี้มันน่า… ใน Java ตอนที่ 1”

RSS for this post's comments

up1 says on 29 Oct 2009 at 11:06 am

ถ้าแบบที่เคยเจอมา แล้วหายากสุดๆ ใน java 1.4 คือ การใช้ Iterator แล้วลืม next

แต่ใน java 5 ขึ้นมามี for each แล้วจึงลดปัญหาตรงนี้ลงไป

Arty says on 29 Oct 2009 at 2:34 pm

ผิดแบบ ลืม i++ ไปเลย T_T
แล้วไม่ได้เป็นกับเฉพาะ java ด้วย

Siros S. says on 29 Oct 2009 at 6:40 pm

สองคนข้างบนนี้ ไม่ได้ใช้ gravatar เหรอครับ

Leave a comment

not published

Recent Posts