การทําซ้ํา
การทําซ้ําหรือการวนลูป แท้จริงแล้วไม่ใช่เรื่องยากเกินไปนัก หากเราเข้าใจ
องค์ประกอบพื้นฐานของการวนลูป ซึ่งมีของอยู่สี่อย่าง
1. การกําหนดค่าเริ่มต้นของตัวแปรต่าง ๆ ก่อนเข้าลูป
2. เงื่อนไขที่จะให้ทําลูป (จะอยู่ก่อนหรืออยู่ด้านหลังคําสั่งที่อยากทําก็ได้)
3. งานที่ต้องการทําซ้ํา
4. การปรับค่าตัวแปรเงื่อนไขลูป (คือการเปลี่ยนค่าตัวแปรที่เกี่ยวพันกับ
เงื่อนไขที่จะให้ทําหรือจบลูป)
การวนลูปในภาษาซี
• มีอยู่สามแบบ
while ( ) { … }
do { … } while ( );
for ( ) { … }
• แบบ while ( ) { … } กับ for ( ) { … } ใช้ทดแทนกันได้เสมอ เพราะ
หลักการคิดและลําดับการทํางานเหมือนกันทุกอย่าง
• ส่วนแบบ do { … } while ( ); จะมีเอกลักษณ์เป็นของตัวเอง เพราะ
ลําดับการคิดแตกต่างจากคนอื่น
รูปแบบทั่วไปของลูป while ( ) { … }
การกําหนดค่าเริ่มต้นตัวแปรก่อนเข้าลูป
while ( เงื่อนไข ) {
งานที่ต้องการทําซ้ํา
การปรับค่าตัวแปรเงื่อนไขลูป
}
… งานหลังจบลูป …
• หลักการทํางานก็คือว่า ถ้าเงื่อนไขของลูปเป็นจริง โปรแกรมจะทําสิ่งที่อยู่ภายในลูป ซึ่งก็คือ ‘งานที่จะให้ทํา’ และ ‘การแก้ไขตัวแปรเงื่อนไขลูป’
• เป็นไปได้ที่เงื่อนไขของลูปจะไม่เป็นจริงตั้งแต่แรก ทําให้ไม่มีการทํางานใด ๆ ภายในลูปเลยแม้แต่ครั้งเดียว
ข้อควรจําเกี่ยวกับลูป while ( ) { … }
การกําหนดค่าเริ่มต้นตัวแปรก่อนเข้าลูป
while ( เงื่อนไข ) {
งานที่ต้องการทําซ้ํา
การปรับค่าตัวแปรเงื่อนไขลูป
}
… งานหลังจบลูป …
• สิ่งใหนที่จะให้ทําซ้ําบ่อย ๆ ต้องอยู่ในลูป สิ่งไหนไม่ต้องทําซ้ําอยู่ข้างนอก
• สิ่งที่คนจํานวนมากลืมคิดก็คือเรื่องการกําหนดค่าตัวแปรก่อนเข้าลูป
• ถ้าลูปไม่มีการแก้ไขตัวแปรที่เกี่ยวกับเงื่อนไขลูปเลย เป็นไปได้มากว่าลูปจะวนไม่รู้จบ เพราะเงื่อนไขที่เป็นจริงในตอนแรกจะเป็นจริงต่อไปหากไม่มีการแก้ไข
ตัวอย่างการทํางานของลูป
โจทย์
จงเขียนโฟลวชาร์ตและโค้ดภาษาซีสําหรับการหาผลบวกของเลขจํานวนเต็มที่มีค่าอยู่ในช่วงปิด 1 ถึง 5 (ช่วงปิดจะรวมเลข 1 และ 5 ด้วย) จากนั้นพิมพ์ผลลัพธ์ออกมาทางจอภาพ (บังคับให้ใช้ลูปค่อย ๆ บวกเลขทีละค่า)
วิเคราะห์
1. ไม่มีการรับข้อมูลเข้าจากผู้ใช้ แต่จะต้องสร้างตัวเลขขึ้นมาเอง
2. งานที่ต้องทําซ้ําแน่ ๆ คือการบวกเลข
3. ต้องมีการนับเลขที่จะบวกเพิ่มขึ้นเรื่อย ๆ เพื่อให้เปลี่ยนตัวบวกจาก 1 ไปเป็น 2, 3, 4 และ 5 ได้
4. เงื่อนไขที่ควรใช้ในการทํางานคือ ‘ตัวบวกต้องอยู่ในช่วง 1 ถึง 5’
ตอบ
void main() {
int sum = 0;
int adder = 1;
while (adder <= 5) {
sum = sum + adder;
adder = adder + 1;
}
printf("%d", sum);
}
ธรรมชาติของปัญหาเกี่ยวกับลูป
• ความยากของเรื่องลูปก็คือว่า ‘มันมีงานแฝงที่ต้องทําเพื่อให้ลูปมันทําตามวัตถุประสงค์ได้’ และงานแฝงนี่แหละที่เป็นสิ่งที่คนจํานวนมากคิดไม่ออก การจะคิดของพวกนี้ได้จะต้องผ่านการฝึกคิดด้วยตัวเองสักระยะหนึ่ง
• เราจะต้องเข้าใจความสัมพันธ์ระหว่างงานหลักที่จะให้ทําเป็นอันดับแรก
• จากนั้นงานหลักจะบอกเราได้ว่า มีอะไรที่โปรแกรมยังขาดไป และเราต้องหาทางเติมเต็มส่วนที่ขาดไปนั้น
• ในตอนแรกที่เรายังไม่มีประสบการณ์เราต้องใช้การสังเกตจากตัวอย่างและยืม ‘กระบวนท่า’ ต่าง ๆ มาประยุกต์ใช้กับปัญหาอื่น จําเป็นต้องใช้ความคิดสร้างสรรค์ในการแก้ปัญหา เพราะปัญหามันไม่เหมือนกันแบบเป๊ะ ๆ เราต้องต้องรู้จักสังเกตและประยุกต์ใช้วิธีการ
สิ่งที่ได้เรียนรู้จากตัวอย่างบวกเลข
• งานนี้เราต้องสังเคราะห์ข้อมูลขึ้นมาสําหรับทําการบวก ตรงนี้เป็นงานแฝง ไม่ปรากฏในตัวปัญหาโดยตรง
• เงื่อนไขในการทําลูปที่บอกว่า adder <= 5 เป็นสิ่งที่ไม่มีการระบุไว้ในตัวปัญหาเลย แต่มันเป็นเงื่อนไขที่เกิดขึ้นมาเพื่อให้ทําวัตถุประสงค์หลักได้ เงื่อนไขใช้ทํางานแฝง ซึ่งก็คือการสังเคราะห์ข้อมูลเข้าขึ้นมา
• เรามีการปรับค่าตัวแปรลูปคือ adder = adder + 1; เพื่อทํางานแฝง และยังให้ผลสอดคล้องกับการสังเคราะห์ค่าขึ้นมาแต่อย่าไปคิดว่าปัญหาทุกอย่างจะเป็นแบบนี้กันหมด ยังมีเทคนิคที่จําเป็นอย่างอื่นอีกมาก และปัญหาจํานวนมากไม่ได้สําเร็จรูปขนาดนี้
• การรู้ว่าอะไรคืองานแฝงและการสร้างเงื่อนไขลูปที่สอดคล้องกัน ต้องอาศัยทั้งความรู้ การวิเคราะห์ และทักษะประสบการณ์ ไม่ง่าย
ตัวอย่าง : บวกเลขในช่วง x ถึง y (โดยที่ y > x)
โจทย์
จงเขียนโค้ดภาษาซีสําหรับการหาผลบวกของเลขจํานวนเต็มที่มีค่าอยู่
ในช่วงปิด x ถึง y (ช่วงปิดจะรวมเลข x และ y ด้วย) โดยที่ค่า x และ y มา
จากผู้ใช้ และ y > x สมมติว่าผู้ใช้ใส่ค่าที่สอดคล้องเงื่อนไขนี้ไม่มีพลาด เมื่อ
ทําการบวกจนเสร็จแล้ว ให้พิมพ์ผลลัพธ์ออกมาทางจอภาพ
วิเคราะห์
1. การบวกควรเริ่มจาก x, x + 1, … ไปสิ้นสุดที่ y
2. ค่า x และ y นี้เป็นตัวกําหนดขอบเขตของตัวบวกและสามารถใช้เป็นเงื่อนไขของลูปในปัญหานี้ได้
3. ในเมื่อโจทย์กําหนดไว้แล้วว่าผู้ใช้ใส่ค่า y ที่มากกว่า x ตลอดเวลาเราไม่ต้องกังวลคอยตรวจค่าว่าจะผิด
ตอบ
void main() {
int x, y;
int sum = 0;
scanf("%d %d", &x, &y);
int adder = x;
while(adder <= y) {
sum = sum + adder;
adder = adder + 1;
}
printf("%d", sum);
}
หรือ
void main() {
int adder, y;
int sum = 0;
scanf("%d %d", &adder, &y);
while(adder <= y) {
sum = sum + adder;
adder = adder + 1;
}
printf("%d", sum);
}
คําสั่งหยุดลูป (break)
บางครั้งเราต้องการออกจากลูปจากช่วงตรงกลาง แทนที่จะการออกจากลูปจากการตรวจเงื่อนไขตอนต้นลูป เช่น หากเราต้องการให้ผู้ใช้ใส่เลขจํานวนเต็มบวกมา 10 ค่าเพื่อให้โปรแกรมหาผลบวกของค่าทั้ง 10 แต่ถ้าผู้ใช้เผลอใส่เลขศูนย์หรือติดลบมา ถือว่าผิดพลาด และโปรแกรมจะหยุดรับค่าทันที
- จากตัวอย่างข้างต้นแสดงว่าเงื่อนไขที่จะหยุดลูปมีสองอย่างคือ (1) ผู้ใช้ใส่ค่าครบ 10 จํานวน และ (2) ผู้ใช้ใส่เลขศูนย์หรือค่าติดลบมา
- เงื่อนไขทั้งสองเป็นอิสระจากกัน ไม่ควรเอามาคิดรวมกันตรงต้นลูปพร้อมกัน
- เงื่อนไขที่เป็นอิสระแบบนี้ถ้าจะนํามารวมกันมันจะซับซ้อนมาก การใช้คําสั่ง break; เพื่อหยุดลูปจะทําให้ตรรกะในการคํานวณง่ายขึ้น
- คําสั่ง break; จะทําให้ลูปที่มันอยู่ข้างในจบการทํางานทันที
การใช้คําสั่ง break;
• คําสั่ง break; โดยตัวของมันเองเป็นการออกจากลูปอย่างไม่มีเงื่อนไข
• ถ้าเราใช้มันตรง ๆ มันก็จะหยุดลูปทุกครั้งไป ลูปจะไม่มีการวนซ้ําเพราะโดนคําสั่ง break; สั่งหยุดทํางานทุกครั้ง เช่น
while (i < 10) {
scanf(“%d”, &x);
break;
sum = sum + x;
i++;
}
จะเห็นได้ว่าคําสั่ง break; อยู่ในลูปโดยไม่อยู่ใต้เงื่อนไขของ if ดังนั้นโปรแกรมนี้หลังจากรับค่าจากผู้ใช้มาเก็บไว้ ก็จะออกจากลูปทันที ไม่มีการวนทําซ้ํา
คําสั่งวกกลับไปต้นลูป (continue)
• บางครั้งจุดที่เราอยากให้โปรแกรมวกกลับไปต้นลูปอาจจะไม่ใช่แค่ตรงด้านท้ายของลูปเท่านั้น เราอาจจะอยากให้มีการวกกลับที่จุดอื่น ๆ ด้วย
• แม้จะเป็นไปได้ที่เราจะแก้ปัญหานี้ผ่านการใช้ if-else ที่ซับซ้อนขึ้น แต่การใช้คําสั่ง continue; เพื่อสั่งให้โปรแกรมวกกลับไปด้านบนของลูปจะทําให้ตรรกะในการคิดดูง่ายขึ้น
• คําสั่ง continue; ไม่ใช่สิ่งที่จําเป็นอย่างยิ่งยวด แต่มันทําให้เรามีอิสระในการวางแผนการคิดในการเขียนโปรแกรมมากขึ้น จึงควรเรียนรู้ไว้
• เช่นเดียวกับ break; การใช้ continue; ที่มีประโยชน์ ต้องใช้คู่กับเงื่อนไขของ if ไม่เช่นนั้นลูปจะวนกลับไปด้านบนทุกครั้ง ไม่มีทางไปถึงคําสั่งที่อยู่หลังจากมัน
การวนลูปแบบไม่จํากัดจํานวนครั้ง
• โดยปรกติลูปจะวนทํางานไปเรื่อย ๆ จนกว่าเงื่อนไขลูปจะเป็นเท็จ
• ส่วนมากเงื่อนไขที่จะให้หยุดลูปเป็นสิ่งที่ระบุไว้ด้านบนของลูป
• ปัญหาบางอย่างไม่เหมาะที่จะคิดแบบนั้น เพราะตรรกะกระบวนการคิดจะซับซ้อนขึ้น เช่น เราต้องการให้โปรแกรมวนรับค่าไปเรื่อย ๆ ไม่จํากัดจนกว่าจะพบว่าผู้ใช้ใส่เลขศูนย์เข้ามา เราอาจจะบอกว่าให้ใช้เงื่อนไขของลูปคือค่าที่ใส่เข้ามา (ซึ่งก็ไม่ผิด) แต่ลูปที่ชัดเจนในตัวเองมักจะใช้คําสั่ง break; เข้าช่วยตรงกลาง และกําหนดเงื่อนไขลูปที่เป็นจริงเสมอ เช่น กําหนดเงื่อนไขว่า 0 < 1 หรือไม่ก็ใส่เลข 1 เข้าไปเลย (เลข 1 มีค่าความจริงเป็นจริงในภาษาซี) ทั้งนี้เพื่อบ่งชี้ว่าโดยธรรมดาแล้วลูปจะวนไปเรื่อย ๆ ไม่จบ เว้นแต่จะพบ เงื่อนไขบางอย่างที่กลางลูป