คำสั่งแต่ละคำสั่งที่ปรากฏในบทก่อนหน้า เป็นคำสั่งที่เกิดการประมวลผลเพียงครั้งเดียวตามลำดับคำสั่งที่ถูกกำหนดโดยผู้เขียนโปรแกรม ในบทนี้ จะกล่าวถึงคำสั่งการวนซ้ำ (iteration) ซึ่งเป็นคำสั่งที่เกิดการประมวลผลให้วนทำงานซ้ำๆ กับคำสั่งหรือชุดคำสั่งในโปรแกรม ภาษา C++ มีคำสั่งการวนซ้ำอยู่ 3 คำสั่ง คือ คำสั่ง while คำสั่ง do…while และคำสั่ง for คำสั่งการวนซ้ำบางครั้งเรียกว่า คำสั่งการวนรอบ หรือ คำสั่งการวนลูป (looping) เนื่องจาก การทำงานที่มีลักษณะวนเป็นรอบๆ นั่นเอง
รูปแบบของคำสั่ง while คือ
while (condition) statement ;
โดยที่ condition หมายถึง นิพจน์จำนวนเต็ม (integral expression) และ statement หมายถึง คำสั่งที่ผู้เขียนโปรแกรมต้องการให้เกิดการประมวลผลในแต่ละรอบ โดยที่ เมื่อใดที่นิพจน์มีค่าไม่เป็นศูนย์ (จริง) คำสั่งจะถูกประมวลผลซ้ำไปเรื่อยๆ จนกระทั่งนิพจน์มีค่าเป็นศูนย์ (เท็จ) คำสั่งจะถูกเพิกเฉย และ โปรแกรมจะย้ายการทำงานไปยังคำสั่งถัดไป เช่นเดียวกับคำสั่งเลือกทำ เงื่อนไขของคำสั่ง while ต้องเขียนอยู่ในเครื่องหมายวงเล็บ ผังงานการทำงานของคำสั่ง while แสดงดังรูป 4-1
ตัวอย่างที่ 4.1 การใช้ while ลูปในการคำนวณหาผลบวกของจำนวนเต็ม 1 ถึง n
int main() { int n, i=1; cout << "Enter a positive integer: "; cin >> n; long sum=0; while (i<=n) sum += i++; cout << "The sum of the first " << n << " integers is " << sum; }
ทดสอบโปรแกรม โดยป้อนค่า n = 8
Enter a positive integer: 8 The sum of the first 8 integers is 36
โปรแกรมนี้จะคำนวณหาผลบวกของ 1+2+3+…+n สำหรับจำนวนเต็ม n ที่ผู้ใช้ป้อนเข้ามา โดยโปรแกรมใช้ตัวแปรท้องถิ่น 3 ตัว คือ n, i และ sum ในแต่ละครั้งที่ลูป while ทำงานซ้ำ ค่าของ i จะถูกนำไปบวกเข้ากับค่า sum แล้วเพิ่มค่าไป i ไป 1 การวนรอบจะหยุดเมื่อ i มีค่ามากกว่า n ดังนั้น n จะเป็นค่าสุดท้ายที่บวกเข้ากับค่า sum ได้ 1+2+3+4+5+6+7+8 = 36
ทดสอบโปรแกรม โดยป้อนค่า n = 100
Enter a positive integer: 100 The sum of the first 8 integers is 5050
ในการประมวลผลครั้งที่สอง ผู้ใช้ได้ป้อน 100 ให้กับ n ทำให้การทำงานของลูป while ต้องทำซ้ำทั้งหมด 100 รอบ เพื่อที่จะคำนวณค่าผลบวกของ 1+2+3+…+98+99+100 = 5050 เป็นค่าของ sum โปรดสังเกตว่า ในโปรแกรมมีการย่อหน้าสำหรับคำสั่งที่อยู่ภายในลูป ซึ่งการกระทำแบบนี้จะทำให้อ่านโปรแกรมได้ง่ายขึ้น โดยเฉพาะโปรแกรมที่มีขนาดใหญ่
ตัวอย่างที่ 4.2 การใช้ลูป while เพื่อคำนวณหาผลบวกของส่วนกลับ (reciprocal)
int main() { int bound; cout << "Enter a positive integer: "; cin >> bound; double sum = 0.0; int i = 0; while ( sum < bound ) sum += 1.0/++i; cout << "The sum of the first " <<i << " reciprocals is " << sum << endl; }
ทดสอบโปรแกรม โดยป้อนค่า bound = 3
Enter a positive integer: 3 The sum of the first 11 reciprocals is 3.01988
โปรแกรมนี้จะคำนวณหาผลบวกของส่วนกลับ $1+1/2+1/3+…+1/n$ เก็บในตัวแปร sum โดยจะหาค่า n mujเป็นจำนวนเต็มที่มีค่าน้อยที่สุดสำหรับ sum < bound โดยค่าของ bound เป็นค่าที่ผู้ใช้ป้อนเข้ามา
ตัวอย่างที่ 4.3 การใช้ลูป while เพื่อทำให้เกิดการคำนวณแบบวนซ้ำ
int main() { double x; cout << "Enter a positive number: "; cin >> x; while ( x > 0 ) { cout << "sqrt(" << x << ") = " << sqrt(x) << endl; cout << "Enter another positive number (or 0 to quit): "; cin >> x; } }
ทดสอบโปรแกรม โดยป้อนค่า x = 49, 3.14159, 100000 และ 0 ตามลำดับ
Enter a positive number: 49 sqrt(49) = 7 Enter another positive number (or 0 to quit): 3.14159 sqrt(3.14159) = 1.77245 Enter another positive number (or 0 to quit): 100000 sqrt(100000) = 316.228 Enter another positive number (or 0 to quit): 0
โปรแกรมนี้จะพิมพ์ค่ารากที่สอง (square root) ของตัวเลขแต่ละตัวที่ผู้ใช้ป้อนเข้ามา การใช้ลูป while จะทำให้การประมวลผลโปรแกรมสามารถมีจำนวนการทำงานวนซ้ำเป็นจำนวนกี่รอบได้ ขึ้นกับเงื่อนไข (x>0) ตัวแปร x เป็นตัวแปรควบคุมการทำงานของลูป โดยที่ตัวแปร x จะมีการเปลี่ยนแปลงค่า โดยรับค่า x ตัวใหม่จากผู้ใช้เป็นผู้ป้อน เราเรียกตัวแปรในลักษณะนี้ว่า ตัวแปรควบคุมลูป (loop control variable)
ในบทที่แล้ว เราเห็นวิธีการใช้คำสั่ง break ในการออกจากคำสั่ง switch (จากตัวอย่างที่ 3.16) ในทำนองเดียวกัน ผู้เขียนโปรแกรมสามารถนำคำสั่ง break นี้มาใช้ควบคุมการวนลูปได้
ตัวอย่างที่ 4.4 การใช้คำสั่ง break เพื่อที่จะหยุดลูป
int main() { int n, i = 1; cout << "Enter a positive integer: "; cin >> n; long sum = 0; while (true) { if (i>n) break; //terminates the loop immediately sum += i++; } cout << "The sum of the first" << n << " integers is " << sum; }
ทดสอบโปรแกรม โดยป้อนค่า n = 100
Enter a positive integer: 100 The sum of the first 100 integers is 5050
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.1 ค่าของตัวแปร i เมื่อเท่ากับค่า n การทำงานของลูปจะหยุด แล้วไปทำงานในส่วนของคำสั่งเพื่อแสดงผลทางหน้าจอในส่วนท้ายของโปรแกรม ในโปรแกรมตัวอย่าง เงื่อนไขที่ใช้ควบคุมลูป while จะเป็นจริง (true) เสมอ ซึ่งหมายความว่าการวนลูปนี้จะวนไปเรื่อยๆ วิธีเขียนคำสั่งในลักษณะนี้เป็นวิธีมาตรฐานในการเขียนโปรแกรมควบคุมลูป while โดยที่ การควบคุมลูปเกิดขึ้นภายในลูป ข้อดีอัสำหรับการใช้คำสั่ง break ภายในลูป คือ จะทำให้ลูปหยุดการทำงานได้ทันที โดยไม่จำเป็นต้องประมวลผลคำสั่งต่างๆ ที่เหลืออยู่ในลูปนั้น
ตัวอย่างที่ 4.5 ตัวเลขฟิโบนาชชิ (Fibonacci)
ตัวเลขฟิโบนาชชิ $F_0,F_1,F_2,F_3,…$ ถูกนิยาม โดยใช้หลักของการแทนค่าไปเรื่อยๆ ตามสมการต่อไปนี้ $$ F_0 = 0 $$ $$ F_1 = 1 $$ $$ F_n = F_{n-1}+F_{n-2} $$
เช่น แทนค่า n ด้วย 2 ในสมการที่สาม จะได้ $$F_2 = F_{2-1} + F_{2-2} = F_1 + F_0 = 1 + 0 = 1$$ ในทำนองเดียวกัน แทนค่า n ด้วย 3 จะได้ $$F_3 = F_{3-1} + F_{3-2} = F_2 + F_1 = 1 + 1 = 2$$ และ แทนค่า n ด้วย 4 จะได้ $$F_4 = F_{4-1} + F_{4-2} = F_3 + F_2 = 2 + 1 = 3$$
ดังนั้น ลำดับของตัวเลขฟิโบนาชชิ 10 ตัวแรก มึค่าเป็น 0, 1, 1, 2, 3, 5, 8, 13, 21 และ 35
int main() { long bound; cout << "Enter a positive integer: "; cin >> bound; cout << "Fibonacci numbers " << bound << ":\n 0, 1"; long f0 = 0, f1 = 1; while (true) { long f2 = f0+f1; if (f2 > bound) break; // terminates the loop immediately cout << ", " << f2; f0 = f1; f1 = f2; } cout << "\n Done! \n"; }
ทดสอบโปรแกรม โดยป้อนค่า bound = 1000
Enter a positive integer: 1000 Fibonacci numbers < 1000 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Done!
โปรแกรมนี้จะพิมพ์ค่าตัวเลขฟิโบนาชชิทั้งหมด ที่น้อยกว่าค่าของ bound ที่ผู้ใช้ป้อนเข้าไปคำสั่งลูป while นี้ ประกอบด้วย 5 คำสั่งภายในลูป เมื่อเงื่อนไข (f2>bound) มีค่าเป็นจริง คำสั่ง break จะถูกประมวลผลทำให้การวนลูปสิ้นสุดทันที โดยไม่ไปประมวลผล 3 คำสั่งสุดท้ายในรอบนั้น
หมายเหตุ การใช้อักขระขึ้นบรรทัดใหม่ ‘\n’ ในข้อความ “:\n0, 1” ซึ่งจะเป็นการพิมพ์เครื่องหมาย : แล้วขึ้นบรรทัดใหม่ เพื่อพิมพ์ 0, 1 ที่ส่วนต้นของบรรทัด
ตัวอย่างที่ 4.6 การใช้ฟังก์ชั่น exit(0)
#include <iostream> #include <cstdlib> // for exit() using namespace std; int main() { long bound; cout << "Enter a positive integer: "; cin >> bound; cout << "Fibonacci numbers " << bound << ":\n0, 1"; long f0 = 0, f1 = 1; while (true) { long f2 = f0+f1; if ( f2 > bound) exit(0); // terminates the loop immediately cout << ", " << f2; f0 = f1; f1 = f2; } cout << "\n Done! \n"; }
ทดสอบโปรแกรม โดยป้อนค่า bound = 1000
Enter a positive integer: 1000 Fibonacci numbers < 1000 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Done!
ฟังก์ชั่น exit ( ) เป็นอีกวิธีหนึ่งที่ใช้ในการหยุดการทำงานของลูป เมื่อฟังก์ชั่นนี้ถูกประมวลผล มันจะหยุดการทำงานของทั้งโปรแกรม เนื่องจากโปรแกรมนี้ไม่มีคำสั่งใดๆต่อท้ายลูป ทำให้การหยุดการทำงานของลูปมีผลเหมือนกับการหยุดการทำงานของทั้งโปรแกรม ทำให้ผลที่ได้จากการประมวลผลโปรแกรมนี้เหมือนกับในตัวอย่างที่ 4.5 แม้ว่า โปรแกรมในตัวอย่างนี้แสดงให้เห็นถึงอีกวิธีหนึ่งที่ใช้ในการออกจากลูปที่ไม่รู้จบ แต่วิธีที่นิยมใช้ในการหยุดลูป คือ การใช้คำสั่ง break ดังแสดงในตัวอย่าง 4.5
ตัวอย่างที่ 4.7 การยกเลิกลูปที่ไม่รู้จบ (infinite loop)
int main() { long bound; cout << "Enter a positive integer: "; cin >> bound; cout << "Fibonacci numbers " << bound << ":\n0, 1"; long f0 = 0, f1 = 1; while (true) //ERROR: INFINITE LOOP! (Press ,ctrl.+c) { long f2 = f0+f1; cout << ", " << f2; f0 = f1; f1 = f2; } cout << "\n Done! \n"; }
ทดสอบโปรแกรม โดยป้อนค่า bound = 1000
Enter a positive integer: 1000 Fibonacci numbers < 1000 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433404437, 701408733, ……… ……………………………
เมื่อปราศจากกลไกในการหยุดการทำงาน ลูปจะถูกประมวลผลไปเรื่อยๆ ไม่รู้จบ การยกเลิกการทำงานของลูปหลังจากที่ได้เริ่มการทำงานไปแล้ว คือการกด <Ctrl>+c (โดยกดปุ่ม Ctrl ค้างแล้วกดปุ่ม c)
รูปแบบของคำสั่ง do…while คือ
do statement while (condition) ;
โดยที่ condition หมายถึง นิพจน์จำนวนเต็ม และ statement หมายถึง คำสั่งที่ผู้เขียนโปรแกรมต้องการให้เกิดการประมวลผลในแต่ละรอบ โดยที่ คำสั่งจะถูกประมวลผล แล้วตรวจสอบว่า นิพจน์มีค่าไม่เป็นศูนย์ (จริง) ก็จะกลับไปประมวลผลคำสั่งซ้ำไปเรื่อยๆ จนกระทั่งนิพจน์มีค่าเป็นศูนย์ (เท็จ) คำสั่งจะถูกเพิกเฉย และ โปรแกรมจะย้ายการทำงานไปยังคำสั่งถัดไป ผังงานการทำงานของคำสั่ง do..while แสดงดังรูป 4-2
คำสั่ง do…while ทำงานเหมือนกับคำสั่ง while ยกเว้นการตรวจสอบเงื่อนไข คำสั่ง do..while จะทำในช่วงท้ายของแต่ละรอบ ในขณะที่คำสั่ง while จะทำก่อนเข้าลูป ดังนั้น การทำงานของคำสั่ง do…while จะทำอย่างน้อย 1 รอบเสมอ ไม่ว่าเงื่อนไขควบคุมจะเป็นเช่นไร ผู้เขียนโปรแกรมสามารถนิยามตัวแปรควบคุมภายในลูปได้
ตัวอย่างที่ 4.8 การใช้คำสั่ง do…while ลูปเพื่อคำนวณหาผลรวมของจำนวนเต็ม 1 ถึง n
int main() { int n, i=0; cout << "Enter a positive integer: "; cin >> n; long sum = 0; do sum += i++; while (i<=n); cout << "The sum of the first " << n << " integers is " << sum; }
ทดสอบโปรแกรม โดยป้อนค่า n = 8
Enter a positive integer: 8 The sum of the first 8 integers is 36
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.1
ตัวอย่างที่ 4.9 ตัวเลขแฟคทอเรียล (factorial)
ตัวเลขแฟคทอเรียล 0!,1!,2!,3!,…ถูกนิยามโดยใช้หลักของการแทนค่าอย่างต่อเนื่องซ้ำๆ ตามสมการ
0! = 1 n! = n(n-1)!
เช่น ให้ n เท่ากับ 1 ในสมการที่สอง จะได้
1! = 1((1-1)!) = 1(0!) = 1(1) = 1
ในทำนองเดียวกัน ถ้าให้ n เท่ากับ 2 จะได้
2! = 2((2-1)!) = 2(1!) = 2(1) = 2
และ ถ้าให้ n เท่ากับ 3 จะได้
3! = 3((3-1)!) = 3(2!) = 3(2) = 6
จากสมการ จะได้ตัวเลขแฟคทอเรียล 7 ตัวแรก เป็น 1, 1, 2, 6, 24, 120 และ 720
int main() { long bound; cout << "Enter a positive integer: "; cin >> bound; cout << "Factorial numbers" << bound << ":\n1"; long f = 1, i = 1; do { cout << ", " << f; f *= ++i; } while (f<bound); }
ทดสอบโปรแกรม โดยป้อนค่า bound = 1000000
Enter a positive integer: 1000000 Factorial numbers < 1000000 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
โปรแกรมนี้จะพิมพ์ค่าตัวเลขแฟคทอเรียลทุกตัวที่มีค่าน้อยกว่าค่าของ bound ซึ่งเป็นค่าที่ผู้ใช้เป็นผู้กำหนด โดยคำสั่ง do…while ในโปรแกรมจะทำงานวนไปเรื่อย ๆ จนกระทั่งเงื่อนไขควบคุม (f < bound) มีค่าเป็นเท็จ
รูปแบบของคำสั่ง for คือ
for (initialization ; condition ; update) statement ;
โดยที่ initialization, condition และ update เป็นนิพจน์ที่อาจมีหรือไม่มีในคำสั่งก็ได้ statement คือ คำสั่งที่ผู้เขียนโปรแกรมต้องการให้เกิดการประมวลผลในแต่ละรอบ ส่วน (initialization ; condition ; update) เป็นส่วนควบคุมการวนรอบ โดยที่ initialization เป็นนิพจน์กำหนดค่าเริ่มต้นให้กับตัวแปรควบคุมของลูปนิพจน์นี้จะถูกประมวลผลก่อนการวนรอบจะเกิดขึ้น condition เป็นนิพจน์เงื่อนไขใช้เพื่อตรวจสอบว่าจะมีการทำงานซ้ำอีกหรือไม่ ในส่วนนี้จะถูกประมวลผลหลังจากการตั้งค่าเริ่มต้นและในทุก ๆ รอบใหม่ของลูป โดยถ้านิพจน์มีค่าไม่เป็นศูนย์ (จริง) คำสั่งต่าง ๆ ภายในลูปจะถูกประมวลผล จนกระทั่งนิพจน์มีค่าเป็นศูนย์ (เท็จ) คำสั่งจะถูกเพิกเฉย และ โปรแกรมจะย้ายการทำงานไปยังคำสั่งถัดไป ส่วน update เป็นนิพจน์ที่กระทำเพื่อปรับเปลี่ยนค่าให้กับตัวแปรควบคุม โดยในส่วนนี้จะถูกประมวลผลทุกครั้งหลังจากที่คำสั่งต่าง ๆ ภายในลูปได้ถูกประมวลผลเสร็จสิ้นในแต่ละรอบแล้ว ลำดับการวนซ้ำที่เกิดขึ้นในคำสั่ง for สรุปได้ ดังนี้
ผังงานการทำงานของคำสั่ง for แสดงดังรูป 4-3
ตัวอย่างที่ 4.10 การใช้ for ลูป เพื่อคำนวณหาผลรวมของจำนวนเต็ม 1 ถึง n
int main() { int n; cout << "Enter a positive integer: "; cin >> n; long sum = 0; for (int i=1; i<=n; i++) sum += i; cout << "The sum of the first" << n << " integers is " << sum; }
ทดสอบโปรแกรม โดยป้อนค่า n = 8
Enter a positive integer: 8 The sum of the first 8 integers is 36
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.1 ในที่นี้นิพจน์การตั้งค่าเริ่มต้น คือ int i = 1 นิพจน์เงื่อนไข คือ i ⇐ n และ นิพจน์การปรับเปลี่ยนค่า คือ i++ ให้สังเกตว่า นิพจน์เหล่านี้มีอยู่ในโปรแกรมตัวอย่างที่ 4.1 ตัวอย่างที่ 4.4 และ ตัวอย่างที่ 4.8 เช่นกัน
ในภาษา C++ มาตรฐาน เมื่อตัวแปรควบคุมลูปถูกนิยามภายในคำสั่ง for (ในตัวอย่างนี้ คือ ตัวแปร i) ขอบเขตของตัวแปรนี้จะถูกจำกัดให้ใช้งานได้เฉพาะภายในลูป for เท่านั้น กล่าวคือ ผู้เขียนโปรแกรมไม่สามารถใช้งานตัวแปร i ภายนอกลูป for ถ้ามีตัวแปรที่มีชื่อเหมือนกันประกาศอยู่ภายนอกคำสั่ง for จะถือว่า ตัวแปรนั้นเป็นคนละตัวกับตัวแปรที่ประกาศภายในคำสั่ง for
ตัวอย่างที่ 4.11 การนำชื่อตัวแปรควบคุมลูป for กลับมาใช้อีก
int main() { int n; cout << "Enter a positive integer: "; cin >> n; long sum = 0; for (int i=1; i<n/2; i++) // scope of this i is in this loop sum += i; for (int i=n/2; i<=n; i++) // scope of this i is in this loop sum += i; cout << "The sum of the first" << n << " integers is " << sum << endl; }
ทดสอบโปรแกรม โดยป้อนค่า n = 8
Enter a positive integer: 8 The sum of the first 8 integers is 36
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.1 คำสั่ง for ทั้งสองในโปรแกรมนี้คำนวณสิ่งเดียวกันกับที่คำสั่ง for ในโปรแกรมในตัวอย่างที่ 4.10 เพียงแต่มีการแบ่งการทำงานออกเป็น 2 ส่วน คือ ทำการหาผลรวมของค่า 1 ถึง n / 2 -1 ในลูปแรก และ สะสมเพิ่มจากค่า n/ 2 ถึง n ในลูปที่สอง โดยที่ คำสั่ง for ทั้งสองมีการนิยามตัวแปรควบคุมเพื่อใช้งานชื่อเหมือนกัน คือ i โดยที่ ตัวแปร i ทั้งสองตัวนี้ถือว่าเป็นตัวแปรคนละตัว เนื่องจาก ตัวแปรทั้งสองถูกประกาศอยู่คนละส่วนในโปรแกรม
ตัวอย่างที่ 4.12 ตัวเลขแฟคทอเรียลอีกครั้ง
int main() { long bound; cout << "Enter a positive integer: "; cin >> bound; cout << "Factorial numbers < " << bound << ":\n1"; long f = 1; for (int i=2; f<=bound; i++) { cout << ", " << f; f *= i; } }
ทดสอบโปรแกรม โดยป้อนค่า bound = 1000000
Enter a positive integer: 1000000 Factorial numbers < 1000000 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.9 โปรแกรมคำสั่ง for นี้มีผลการทำงานเหมือนกับโปรแกรมคำสั่ง do…while เนื่องจาก มีการประมวลผลคำสั่งต่างๆ ที่เหมือนกัน หลังจากที่กำหนดค่าเริ่มต้นของ f เป็น 1 ทั้งสองโปรแกรมได้กำหนดค่าให้ i เป็น 2 แล้วประมวลผลทั้ง 5 คำสั่งวนไปเรื่อยๆ คือ พิมพ์ค่า f, คูณ f ด้วย i , เพิ่มค่า i, ตรวจสอบเงื่อนไข (f ⇐bound) และ หยุดการทำงานของลูปเมื่อเงื่อนไขเป็นเท็จคำสั่ง for เป็นคำสั่งที่มีความยืดหยุ่นในการเขียน เมื่อเปรียบเทียบกับคำสั่ง while และ คำสั่ง do..while ซึ่งจะแสดงในตัวอย่างต่อไป
ตัวอย่างที่ 4.13 การใช้คำสั่ง for แบบนับถอยหลัง
int main() { for (int i=10; i>0; i--) cout << " " << i; }
ทดสอบโปรแกรม
10 9 8 7 6 5 4 3 2 1
โปรแกรมนี้จะพิมพ์จำนวนเต็ม 10 ค่าแรกในลำดับที่กลับกัน
ตัวอย่างที่ 4.14 การใช้คำสั่ง for แบบที่ขนาดของขั้นการนับมีค่ามากกว่าหนึ่ง
int main() { long n; cout << "Enter a positive integer: "; cin >> n; if (n<2) cout << n << " is not prime." << endl; else if (n<4) cout << n << "is prime." << endl; else if (n%2 == 0) cout << n << " = 2*" << n/2 << endl; else { for (int d=3; d<=n/2; d+=2) if (n%d == 0) { cout << n << " = " << d << "*" << n/d << endl; exit(0); } cout << n << " is prime." << endl; }; }
ทดสอบโปรแกรม โดยป้อนค่า n = 101
Enter a positive integer: 101 101 is prime.
ทดสอบโปรแกรม โดยป้อนค่า n = 975313579
Enter a positive integer: 975313579 975313579 = 17*57371387
โปรแกรมนี้จะหาว่าตัวเลขที่ผู้ใช้ป้อนเข้ามาเป็นจำนวนเฉพาะหรือไม่ ให้สังเกตว่าคำสั่ง for มีการเพิ่มขึ้นของตัวแปรควบคุม d ขึ้นครั้งละ 2 ในแต่ละรอบ
ตัวอย่างที่ 4.15 การใช้ sentinel เพื่อควบคุมคำสั่ง for
int main() { int n, max; cout << "Enter positive integers (0 to quit): "; cin >> n; for ( max=n; n>0;) { if (n>max) max = n; cin >> n; } cout << "max = " << max << endl; }
ทดสอบโปรแกรม โดยป้อนค่า n เป็น 44, 77, 55, 22, 99, 33, 11, 66, 88 และ 0 ตามลำดับ
Enter positive integers (0 to quit): 44 77 55 22 99 33 11 66 88 0 max = 99
โปรแกรมนี้จะหาค่าที่มากที่สุดของชุดจำนวนเต็มที่ผู้ใช้ป้อนเข้ามา คำสั่ง for จะควบคุมค่าของตัวแปรควบคุม n ตามค่าที่ผู้ใช้ป้อนเข้ามา ซึ่งการวนรอบจะกระทำไปเรื่อย ๆ จนกระทั่งค่า n มีค่าน้อยกว่าหรือเท่ากันกับ 0 การควบคุมการวนรอบตามค่าที่ผู้ใช้ป้อนเข้ามา เรียกว่า เซนทิเนล (sentinel)
ให้สังเกตว่า กลไกควบคุมการวนรอบ (max = n ; n> o;) ภายในคำสั่ง for ไม่มีส่วนของการปรับเปลี่ยน และ การตั้งค่าเริ่มต้น max = n ไม่มีส่วนที่ใช้ในการประกาศตัวแปร ทั้งนี้เนื่องจาก ตัวแปร max ได้ถูกประกาศไว้ก่อนคำสั่ง for ซึ่งทำให้สามารถใช้งานตัวแปรนี้ภายนอกลูปได้ เช่นในส่วนของการแสดงผลที่ท้ายโปรแกรม
ตัวอย่างที่ 4.16 การใช้ loop invariant เพื่อพิสูจน์ว่า for ลูป ถูกต้อง
int main() { int n, min; cout << "Enter positive integers (0 to quit): "; cin >> n; for ( min=n; n>0;) { if (n<min) min = n; //INVARIANT: min<=n for all n,and min equals one of the n cin >> n; } cout << "min= " << min << endl; }
ทดสอบโปรแกรม โดยป้อนค่า n เป็น 44, 77, 55, 22, 99, 33, 11, 66, 88 และ 0 ตามลำดับ
Enter positive integers (0 to quit): 44 77 55 22 99 33 11 66 88 0 max = 11
โปรแกรมนี้จะหาค่าที่น้อยที่สุดของชุดจำนวนเต็มบวกที่ผู้ใช้ป้อนเข้าไป ในทำนองเดียวกับโปรแกรมในตัวอย่างที่ 4.15 บรรทัดหมายเหตุ (comment) ที่ในคำสั่ง for แสดงตำแหน่งของ loop invariant ในคำสั่ง for โดย loop invariant เป็นการพิสูจน์ให้เห็นคุณสมบัติ 2 ประการ คือ (1) เงื่อนไขเป็นจริง ณ ตรงนั้นสำหรับทุกๆ การวนรอบของลูป (2) ความจริงที่ว่าเงื่อนไขเป็นจริงเมื่อลูปถูกระงับ พิสูจน์ให้เห็นว่าลูปทำงานถูกต้องสมบูรณ์แบบ ในกรณีนี้ เงื่อนไขที่ว่า min มีค่าน้อยกว่าหรือเท่ากับ n สำหรับทุกค่า n จะเป็นจริงเสมอ เนื่องจากคำสั่ง if จะทำการปรับปรุงค่าของ min ใหม่ ถ้าพบว่าค่าของ n ที่ป้อนเข้ามาตัวสุดท้ายมีค่าน้อยกว่าค่าของ min ที่มีอยู่เดิม และ เงื่อนไขที่ว่า min จะมีค่าเท่ากับค่าใดค่าหนึ่งของ n ก็จะเป็นจริงเสมอ เนื่องจาก min ได้ถูกตั้งค่าเริ่มต้นเป็นค่า n ตัวแรก และ จะถูกแทนที่(ในบางครั้ง)ด้วยค่า n ตัวใหม่และ สุดท้าย ความจริงที่ว่าเงื่อนไขจะเป็นจริงเมื่อลูปถูกระงับ หมายความว่า min จะเป็นค่าที่น้อยที่สุดของจำนวนเต็มบวกที่ป้อนเข้ามา และ ผลที่ได้นั้นก็คือจุดประสงค์ของคำสั่ง for นี้
ตัวอย่างที่ 4.17 จำนวนตัวแปรควบคุมที่มีมากกว่าหนึ่งใน for ลูป
int main() { for (int m=95, n=11; m%n>0; m-=3, n++) cout << m << "%" << n << " = " << m%n << endl; }
ทดสอบโปรแกรม
95%11 = 7 92%12 = 8 89%13 = 11 86%14 = 2 83%15 = 8
คำสั่ง for ในโปรแกรมนี้มีการกำหนดตัวแปรควบคุม 2 ตัว โดยตัวแปรควบคุมทั้งสองตัว คือ m และ n ถูกประกาศ และ ตั้งค่าเริ่มต้นในกลไกของการควบคุมของคำสั่ง for หลังจากนั้นค่า m จะถูกลดลงทีละ 3 ในขณะที่ค่า n จะถูกเพิ่มขึ้นทีละ 1 ในแต่ละรอบ ทำให้เกิดคู่ลำดับของ (m,n) เป็น (95,11) , (92,12) , (89,13) , (86,14) , (83,15) , (80,16) โดยที่ลูปจะถูกระงับด้วยคู่ของ (80,16) เนื่องจาก 80 หาร 16 ลงตัว
ตัวอย่างที่ 4.18 คำสั่ง for ซ้อน
#include <iomanip> //defines setw() #include <iostream> //defines cout using namespace std; int main() { for (int x=1; x<=12; x++) { for (int y=1; y<=12; y++) cout << setw(4) << x*y; cout << endl; } }
ทดสอบโปรแกรม
1 2 3 4 5 6 7 8 9 10 11 12 2 4 6 8 10 12 14 16 18 20 22 24 3 6 9 12 15 18 21 24 27 30 33 36 4 8 12 16 20 24 28 32 36 40 44 48 5 10 15 20 25 30 35 40 45 50 55 60 6 12 18 24 30 36 42 48 54 60 66 72 7 14 21 28 35 42 49 56 63 70 77 84 8 16 24 32 40 48 56 64 72 80 88 96 9 18 27 36 45 54 63 72 81 90 99 108 10 20 30 40 50 60 70 80 90 100 110 120 11 22 33 44 55 66 77 88 99 110 121 132 12 24 36 48 60 72 84 96 108 120 132 144
โปรแกรมนี้จะพิมพ์ตารางแม่สูตรคูณ โดยการวนลูปแต่ละรอบของลูปนอก x จะพิมพ์แถวหนึ่งแถวของตารางแม่สูตรคูณ ยกตัวอย่างเช่น ในรอบแรกเมื่อ x=1 ลูปใน y จะวนซ้ำ 12 ครั้งโดยจะพิมพ์ค่า 1*y ของค่าจาก 1 ถึง 12 หลังจากนั้นในรอบที่สองของลูปนอก x เมื่อ x=2 ลูปใน y ก็วนซ้ำอีก 12 รอบเช่นเดิม โดยในครั้งนี้จะพิมพ์ค่า 2*y สำหรับทุกค่า y จาก 1 ถึง 12 คำสั่ง cout « endl ที่แยกออกมาอยู่ภายในลูปนอก และอยู่ภายนอกลูปใน เพื่อที่จะทำให้เกิดบรรทัดหนึ่งใหม่ เมื่อสิ้นสุดการวนรอบแต่ละครั้งของลูปนอก
โปรแกรมนี้ใช้ stream manipulator setw ในการกำหนดความกว้างของช่วงข้อมูลที่จะพิมพ์ออกไปสำหรับจำนวนเต็มแต่ละตัว โดยที่นิพจน์ setw(4) หมายถึง กำหนดการแสดงผลข้อมูลอย่างน้อย 4 ตำแหน่ง สำหรับข้อมูลที่จะแสดงตัวถัดไป การจัดการแบบนี้ทำให้ข้อมูลที่จะแสดงออกไปถูกจัดอยู่ในรูปตารางที่อ่านง่ายขนาด 12 สดมภ์ โดยที่ ข้อมูลจำนวนเต็มแต่ละตัวจะถูกแสดงชิดขวา การจัดการข้อมูลการแสดงผล setw ( ) ถูกนิยามอยู่ในไฟล์ส่วนหัว <iomanip> เพราะฉะนั้น โปรแกรมนี้ต้องมีคำสั่งประกาศ
#include <iomanip>
ด้วย นอกเหนือจากไฟล์ส่วนหัว <iostream>
ตัวอย่างที่ 4.19 การตรวจสอบ loop invariant
#include <cmath> // defines pow() and log() #include <iostream> // defines cin and cout #include <iomanip> // defines setw() using namespace std; int main() { ong n; cout << "Enter a positive integer: "; cin >> n; int d = 0; // the discrete binary logarithm of n double p2d = 1; // =2^d for (int i=n; i>1; i/=2,d++) { // INVARIANT: 2^d <=n/I < 2*2^d p2d = pow(2,d); // =2^d cout << setw(2) << p2d << " <= " << setw(2) << n/i << " < " << setw(2) << 2*p2d << endl; } p2d = pow(2,d); // =2^d cout << setw(2) << p2d << " <= " << setw(2) << n << " < " << setw(2) << 2*p2d << endl; cout << " The discrete binary logarithm of " << n << " is " << d << endl; double lgn = log(n)/log(2); // base 2 logarithm of n cout << "The continuous binary logarithm of " << n << "is " << lgn << endl; }
ทดสอบโปรแกรม โดยป้อนค่า n = 63
Enter a positive integer: 63 1 <= 1 < 2 2 <= 2 < 4 4 <= 4 < 8 8 <= 8 < 16 16 <= 16 < 32 32 <= 32 < 64 The discrete binary logarithm of 63 is 5 The continuous binary logarithm of 63 is 5.97728
โปรแกรมนี้จะคำนวณและพิมพ์ จำนวนเต็มบวกที่มากที่สุดที่น้อยกว่าหรือเท่ากับค่าลอการิทึมฐานสองของจำนวนเต็มที่ผู้ใช้ป้อนเข้าไป (discrete binary logarithm) โดยที่จะมีการตรวจสอบ loop invariant แล้วพิมพ์ค่าที่เกี่ยวข้องในแต่ละรอบ
ค่า discrete binary logarithm คำนวณจาก จำนวนครั้งที่จำนวนเต็มที่ป้อนเข้ามาสามารถหารด้วย 2 ไปได้เรื่อยๆ ก่อนจะมีค่าเป็น 1 ดังนั้นคำสั่ง for จึงกำหนดค่าเริ่มต้นของตัวแปร i ให้เป็น n หลังจากนั้น จึงหาร i ด้วย 2 ในแต่ละรอบ เมื่อสิ้นสุดการวนรอบ ค่าของ c จะเป็นค่าของ discrete binary logarithm ของ n
นอกเหนือจาก การใช้ฟังก์ชัน setw ( ) ที่ถูกนิยามในไฟล์ส่วนหัว <iomanip> แล้ว โปรแกรมยังได้ใช้ฟังก์ชัน log ( ) ซึ่งถูกนิยามในไฟล์ส่วนหัว <cmath> ซึ่งฟังก์ชันนี้จะส่งค่าลอการิทึมฐาน e ของ n (log(n) = loge n = ln n) ซึ่งจะถูกใช้ในนิพจน์ log(n) / log(2) เพื่อที่จะคำนวณหาค่าลอการิทึมฐาน 2 ของ n : log2n = lg n = (ln n) / (ln 2) ผลลัพธ์ที่พิมพ์ออกมาจะเปรียบเทียบค่า discrete binary logarithm กับค่า continuous binary logarithm โดยที่ค่าแรกจะมีค่าเท่ากับค่าหลังที่ถูกปัดเศษลงให้ใกล้กับจำนวนเต็มมากที่สุด
loop invariant ในตัวอย่างนี้ คือ เงื่อนไข 2^d ⇐ n / i < 2 * 2^d ซึ่งถูกตรวจสอบจากการพิมพ์ค่านิพจน์ทั้งสาม p2d, n และ 2 * p2d โดยที่ p2d ได้มาจากการคำนวณด้วยฟังก์ชันยกกำลัง pow ( ) ซึ่งถูกนิยามในไฟล์ส่วนหัว <cmath>
เราสามารถพิสูจน์ได้ว่าคำสั่ง for นี้ จะคำนวณหาค่า discrete binary logarithm ได้ถูกต้องเสมอ ในตอนเริ่มต้น d = 0 และ i = n ดังนั้น 2d = 20 = 1, n / i = n / n = 1 และ 2×2d = 2×20 = 2 ดังนั้น 2d ⇐ n / i < 2×2d ในแต่ละรอบค่า d จะเพิ่มขึ้น และ i จะถูกทอนลงครึ่งหนึ่ง ทำให้ค่า n / i จะเพิ่มขึ้นเป็นสองเท่า ดังนั้นเงื่อนไข 2d ⇐ n / i < 2×2d จะไม่เปลี่ยนแปลง เช่น มันเป็นจริงในตอนแรก และมันคงเป็นจริงตลอดช่วงของการวนรอบ เมื่อลูปสิ้นสุดการทำงาน ค่า i = 1 ทำให้เงื่อนไขเปลี่ยนเป็น 2d ⇐ n / 1 < 2×2d ซึ่งมีค่าเท่ากับ 2d ⇐ n < 2×2d ลอการิทึมของนิพนจ์นี้คือ d = lg(2d) ⇐ lg n < lg(2d+1) = d + 1 ทำให้ d เป็นจำนวนเต็มที่มากที่สุดที่ ⇐ lg n
ในบทที่ 3 แสดงการใช้คำสั่ง break ในคำสั่ง switch เพื่อให้จบการทำงานของคำสั่ง switch ในทันที แล้วไปประมวลผลคำสั่งถัดไป ในทำนองเดียวกัน ผู้เขียนโปรแกรมสามารถใช้คำสั่ง switch ในลูปได้ โดยที่ เมื่อคำสั่งนี้ถูกประมวลผล มันจะหยุดการทำงานของลูป และ ออกจากการวนรอบออกไปประมวลผลคำสั่งถัดไปทันที
ตัวอย่างที่ 4.20 การใช้คำสั่ง break เพื่อหยุดลูป
int main() { int n, i = 1; cout << "Enter a positive integer: "; cin >> n; long sum = 0; while (true) { if (i>n) break; sum += i++; } cout << "The sum of the first " << n << " integers is " << sum; }
ทดสอบโปรแกรม โดยป้อนค่า n = 8
Enter a positive integer: 8 The sum of the first 8 integers is 36
โปรแกรมนี้จะมีผลการทำงานเช่นเดียวกับตัวอย่างที่ 4.1 แต่ใช้คำสั่ง break ในการควบคุมลูป โดยลูปจะวนทำงานไปเรื่อย ๆ ตามเงื่อนไข (true) แต่ถ้าเมื่อใดที่นิพจน์ (i>n) มีค่าเป็นไม่เป็นศูนย์ (จริง) คำสั่ง break จะถูกประมวลผล ก่อให้เกิดการหยุดการวนรอบ
คำสั่ง break ทำให้เกิดความยืดหยุ่นในการควบคุมลูป ซึ่งตามปกติคำสั่ง while หรือ do…while หรือ for จะหยุดการทำงานเฉพาะในส่วนเริ่มต้นหรือส่วนสุดท้ายของลูป แต่คำสั่ง break สามารถปรากฏที่ใดก็ได้ในส่วนคำสั่งภายในลูป กล่าวคือ ผู้เขียนโปรแกรมสามารถเขียนคำสั่ง ให้หยุดการทำงานของลูปที่ใดก็ได้ภายในลูป ซึ่งจะแสดงให้เห็นดังตัวอย่างต่อไปนี้
ตัวอย่างที่ 4.21 การควบคุมการนำเข้าข้อมูลแบบ sentinel
int main() { int n, count = 0, sum = 0; cout << "Enter positive integers (0 to quit): " << endl; for (;;) // "forever" { cout << "\t" << count+1 << " : "; cin >> n; if (n<=0) break; ++count; sum += n; } cout << "The average of those " << count << " positive numbers is " << float(sum)/count << endl; }
ทดสอบโปรแกรม โดยป้อนค่า n = 4, 7, 1, 5, 2 และ 0 ตามลำดับ
Enter positive integers (0 to quit): 1: 4 2: 7 3: 1 4: 5 5: 2 6: 0 The average of those 5 positive numbers is 3.8
โปรแกรมนี้จะนำเข้าชุดข้อมูลจำนวนเต็มบวก โดยตัวสุดท้าย คือ 0 ซึ่งหมายความว่าหมดข้อมูล แล้วพิมพ์ค่าเฉลี่ยของชุดข้อมูล ไม่รวมค่า 0) ทางจอภาพ เมื่อค่าตัวแปร n มีค่าเป็น 0 คำสั่ง break จะถูกประมวลผล ทำให้ลูปหยุดการทำงาน แล้วย้ายการทำงานไปยังคำสั่งการแสดงผล ถ้าไม่มีคำสั่ง break ผู้เขียนโปรแกรมต้องเขียนคำสั่ง ++count ไว้ในส่วนที่เป็นเงื่อนไขควบคุมการวนรอบ ไม่เช่นนั้น ผู้เขียนโปรแกรมต้องเขียนคำสั่งให้ปรับปรุงค่าของตัวแปร count โดยให้ค่าในตัวแปร count ลดลง 1 ก่อนนำไปแสดงผล หรือ กำหนดให้ตัวแปร count มีค่าเริ่มต้นเป็น -1
ให้สังเกตว่า ทั้งสามส่วนของกลไกควบคุมในคำสั่ง for นั้นว่าง ( ; ; ) โครงสร้างนี้ หมายถึง การควบคุมให้เกิดการวนรอบไม่รู้จบในคำสั่ง for และ ถ้าไม่มีคำสั่ง break ลูปนี้จะวนรอบแบบไม่รู้จบ
คำสั่ง break เมื่อถูกใช้ในลูปที่ซ้อนกัน ผลของมันจะกระทบในลูปที่มันถูกกำหนดอยู่เท่านั้น เช่น ถ้าคำสั่ง break ปรากฏอยู่ในลูปใน ส่วนลูปนอกก็ยังคงทำงานต่อไป โดยไม่ได้รับผลจากคำสั่ง break นั้นเลย ซึ่งแสดงให้เห็นดังตัวอย่างต่อไปนี้
ตัวอย่างที่ 4.22 การใช้คำสั่ง break กับลูปที่ซ้อนกัน
int main() { for (int x=1; x<=12; x++) { for (int y=1; y<=12; y++) if (y>x) break; else cout << setw(4) << x*y; cout << endl; } }
ทดสอบโปรแกรม
1 2 4 3 6 9 4 8 12 16 5 10 15 20 25 6 12 18 24 30 36 7 14 21 28 35 42 49 8 16 24 32 40 48 56 64 9 18 27 36 45 54 63 72 81 10 20 30 40 50 60 70 80 90 100 11 22 33 44 55 66 77 88 99 110 121 12 24 36 48 60 72 84 96 108 120 132 144
เนื่องจาก ตัวดำเนินการคูณมีความสัมพันธ์กันตามกฎของการสลับที่ (เช่น 3´4 = 4´3) ตารางแม่สูตรคูณส่วนใหญ่จึงมักจะละส่วนด้านบนที่เหนือแนวทะแยงมุมหลัก โปรแกรมนี้ได้ถูกพัฒนาต่อจากตัว อย่างที่ 4.18 เพื่อทำการพิมพ์ตารางแม่สูตรคูณแบบสามเหลี่ยม
เมื่อ y > x การทำงานในลูปใน y จะถูกระงับ และไปเริ่มต้นในรอบใหม่ของลูปนอก x ยกตัวอย่างเช่น เมื่อ x = 3 ลูป y จะวนทำ 3 ครั้ง (ด้วยค่า y = 1, 2 และ 3 ตามลำดับ) เพื่อพิมพ์ค่า 3 6 9 โดยในรอบที่ 4 ของลูป y เงื่อนไข (y>x) เป็นจริง คำสั่ง break จึงทำให้ลูปหยุด และย้ายไปทำคำสั่ง cout « endl (ซึ่งอยู่นอกลูป y) หลังจากนั้นลูปรอบนอก x จะไปเริ่มทำงานในรอบที่ 4 ด้วยค่า x = 4
คำสั่ง break จะข้ามคำสั่งที่เหลือในลูป แล้วกระโดดไปทำคำสั่งถัดไปนอกลูปทันที คำสั่ง continue ก็เช่นกัน มันจะข้ามคำสั่งที่เหลือในลูปนั้น แต่แทนที่จะระงับการทำงานของลูปทั้งหมดลง มันจะย้ายการทำงานไปยังรอบถัดไปของลูปนั้นๆ กล่าวคือ มันจะไปทำงานต่อในรอบถัดไป หลังจากข้ามคำสั่งต่างๆในลูปที่เหลือในรอบนั้น
ตัวอย่างที่ 4.23 การใช้คำสั่ง continue และ คำสั่ง break
int main() { int n; for (;;) { cout << "Enter int: "; cin >> n; if (n%2 == 0) continue; if (n%3 == 0) break; cout << "\tBottom of loop.\n"; } cout << "\tOutside of loop\n"; }
ทดสอบโปรแกรม
Enter int: 7 Bottom of loop. Enter int: 4 Enter int: 9 Outside of loop.
โปรแกรมนี้เปรียบเทียบผลการทำงานของคำสั่ง continue และ คำสั่ง break เมื่อตัวแปร n มีค่าเป็น 7 เงื่อนไขของ if ทั้งสองตัวเป็นเท็จ ทำให้การทำงานย้ายไปยังส่วนท้ายของลูป เมื่อ n มีค่าเป็น 4 เงื่อนไขของ if ตัวแรกเป็นจริง (4 หาร 2 ลงตัว) ทำให้การทำงานข้ามคำสั่งที่เหลือแล้วกระโดดไปยังส่วนต้นของลูปอีกครั้งทันที เพื่อที่จะเริ่มการทำงานในรอบใหม่ เมื่อ n มีค่าเป็น 9 เงื่อนไขของ if ตัวแรกเป็นเท็จ (9 หาร 2 ไม่ลงตัว) แต่เงื่อนไขตัวที่สองของ if เป็นจริง (9 หาร3 ลงตัว) ทำให้การวนรอบสิ้นสุด และหลุดออกมาจากลูป เพื่อไปประมวลผลคำสั่งถัดไปที่อยู่นอกลูป
คำสั่ง break และ คำสั่ง continue เป็นคำสั่งให้โปรแกรมย้ายการประมวลผลไปทำงานในส่วนอื่น นอกเหนือจากลำดับการทำงานตามปกติในคำสั่งนั้น ส่วนจุดหมายปลายทางของคำสั่งที่จะย้ายการทำงานไป จะแตกต่างกันในแต่ละคำสั่ง คือ คำสั่ง break จะไปย้ายการทำงานไปที่คำสั่งแรกนอกลูป ในขณะที่ คำสั่ง continue จะย้ายการทำงานไปส่วนเริ่มต้นของลูปในรอบถัดไป คำสั่งเหล่านี้จัดเป็นคำสั่งในกลุ่ม คำสั่งข้ามกระโดด (jump statements) เนื่องจาก มีผลทำให้เกิดการเปลี่ยนแปลงการทำงานไปยังคำสั่งที่อยู่ในตำแหน่งอื่นๆ ที่ไม่ใช่คำสั่งถัดไป
คำสั่ง goto ก็เป็นอีกหนึ่งคำสั่งข้ามกระโดด ที่ปลายทางของคำสั่งจะถูกกำหนดด้วยฉลาก (label) ซึ่งระบุตำแหน่งโดยผู้เขียนโปรแกรม ฉลากเป็นชื่อที่ตามด้วยเครื่องหมาย : (colon) โดยที่ ผู้เขียนโปรแกรมสามารถวางฉลากไว้หน้าคำสั่งที่ต้องการให้มีการย้ายตำแหน่งไปตามการทำงานของคำสั่ง goto การทำงานของฉลากในภาษา C++ จะคล้ายกับการทำงานของคำสั่ง case ในคำสั่ง switch คือ จะบ่งบอกตำแหน่งปลายทางของการกระโดด
ตัวอย่างที่ 4.22 แสดงให้เห็นถึงการทำงานของคำสั่ง break ในลูปที่ซ้อนกัน การทำงานจะออกมาจากลูปในสุดที่คำสั่ง break อยู่ และ กระโดดไปยังลูปในถัดไปที่ซ้อนกัน ถ้าต้องการย้ายการทำงานจากลูปในสุดไปยังลูปนอก ต้องอาศัยคำสั่ง goto ดังจะเห็นจากตัวอย่างต่อไปนี้
ตัวอย่างที่ 4.24 การใช้คำสั่ง goto เพื่อออกจากลูปที่ซ้อนกัน
int main() { const int N = 5; for (int i=0; i<N; i++) { for (int j=0; j<N; j++) { for (int k=0; k<N; k++) if (i+j+k > N) goto esc; else cout << i+j+k << " "; cout << "* "; } esc: cout << "." << endl; //inside i loop, outside j loop } }
ทดสอบโปรแกรม
0 1 2 3 4 * 1 2 3 4 5 * 2 3 4 5 . 1 2 3 4 5 * 2 3 4 5 . 2 3 4 5. 3 4 5 . 4 5 .
เมื่อการทำงานมาถึงคำสั่ง goto ซึ่งอยู่ภายในลูป k (ลูปในสุด) โปรแกรมจะกระโดดไปยังคำสั่งที่มีฉลากที่อยู่ส่วนท้ายของลูป i (ลูปนอก) เนื่องจากคำสั่งนั้นเป็นคำสั่งสุดท้ายในลูป i การทำงานจึงวนกลับไปยังส่วนต้นลูป i อีกรอบหนึ่ง เมื่อ i และ j มีค่าเป็น 0 ลูป k จะวนซ้ำ 5 ครั้ง เพื่อแสดงค่า 0 1 2 3 4 ตามด้วยเครื่องหมาย * หลังจากนั้น j จะเพิ่มขึ้นเป็น 1 และ ลูป k ก็วนอีก 5 รอบเพื่อพิมพ์ 1 2 3 4 5 แล้วตามด้วย * หลังจากนั้น j จะเพิ่มขึ้นเป็น 2 และลูป k ก็วนอีก 4 รอบเพื่อพิมพ์ 2 3 4 5 แต่รอบถัดไปของลูป k i มีค่าเป็น 0 j มีค่าเป็น 2 และ k มีค่าเป็น 4 ทำให้ i+j+k = 6 มีผลให้คำสั่ง goto ทำงานเป็นครั้งแรก ทำให้การทำงานกระโดดไปยังคำสั่งที่มีฉลาก esc: เพื่อพิมพ์จุด แล้วขึ้นบรรทัดใหม่ สังเกตว่าทั้งลูป j และลูป k จะถูกระงับก่อนที่จะจบการทำงานครบทุกรอบ
ต่อมาในรอบ i = 1 ลูป j ก็มาเริ่มทำงานอีกครั้งด้วย j = 0 ลูป k จะทำงาน 5 รอบเพื่อพิมพ์ 1 2 3 4 5 ตามด้วย * หลังจากนั้น j จะเพิ่มขึ้นเป็น 1 และลูป k ก็วนอีก 4 รอบเพื่อพิมพ์ 2 3 4 5 หลังจากนั้นในรอบถัดไปของลูป k i มีค่าเป็น 1 j มีค่าเป็น 2 และ k มีค่าเป็น 3 ทำให้ i+j+k = 6 มีผลให้คำสั่ง goto ถูกประมวลผลเป็นครั้งที่สอง และ อีกครั้งที่การทำงานกระโดดไปทำที่คำสั่งที่มีฉลากทันที เพื่อพิมพ์จุด แล้วขึ้นบรรทัดใหม่
ใน 3 รอบถัดมาของลูป i ลูปใน k จะไม่มีทางที่จะวนจนครบรอบการทำงานของมัน เนื่องจาก i+j+k ยังไงก็มีค่าเกิน 5 (เนื่องจาก i มีค่ามากกว่า 2 ) ทำให้ไม่มีการพิมพ์ * อีกต่อไป
ให้สังเกตว่า คำสั่งที่มีฉลากสามารถปรากฏอยู่ภายในลูป หรือ ภายนอกของลูปก็ได้ ซึ่งการกระทำในกรณีหลังนี้จะทำให้คำสั่ง goto ระงับการทำงานของลูปทั้งหมดที่ซ้อนกัน โดยทั่วไป การเขียนคำสั่งที่มีฉลากจะเขียนเยื้องออกไปซ้ายกว่าปกติ เพื่อให้มองเห็นได้ง่ายขึ้น ซึ่งถ้าคำสั่งนั้นไม่ได้มีฉลาก เช่น
cout << "." <<endl;
แทนที่
esc: cout << "." <<endl;
ตัวอย่างที่ 4.24 แสดงให้เห็นถึงวิธีในการออกจากลูปที่ซ้อนกัน อีกวิธีหนึ่งที่สามารถออกจากลูปได้ คือ การใช้ flag ซึ่ง flag ก็คือ ตัวแปรชนิดตรรกะที่มีการตั้งค่าเริ่มต้นเป็นเท็จ และ ภายหลังเมื่อตรวจพบว่าเงื่อนไขบางอย่างเป็นจริง ค่า flag ก็จะถูกเปลี่ยนให้มีค่าเป็นจริง เพื่อบ่งชี้ถึงเป็นภาวะพิเศษ โดย ผู้เขียนโปรแกรมสามารถเขียนการทำงานของโปรแกรม ให้ถูกขัดจังหวะเมื่อค่าของ flag มีค่าเป็นจริง ซึ่งจะแสดงให้เห็นในตัวอย่างต่อไปนี้
ตัวอย่างที่ 4.25 การใช้ flag เพื่อออกจากลูปที่ซ้อนกัน
int main() { const int N = 5; bool done = false; for (int i=0; i<N; i++) { for (int j=0; j<N && !done; j++) { for (int k=0; k<N && !done; k++) if (i+j+k > N) done = true; else cout << i+j+k << " "; cout << "* "; } cout << "." << endl; //inside i loop, outside j loop done = false; } }
ทดสอบโปรแกรม
0 1 2 3 4 * 1 2 3 4 5 * 2 3 4 5 * . 1 2 3 4 5 * 2 3 4 5 * . 2 3 4 5 *. 3 4 5 * . 4 5 * .
โปรแกรมนี้มีผลรันคล้ายกับในตัวอย่างที่ 4.24 เมื่อ flag ที่ชื่อว่า done มีค่าเป็นจริง ทั้งลูปในสุด k และ ลูปกลาง j จะถูกระงับ ออกไปยังลูปนอก i เพื่อจบการทำงานในรอบนั้น ด้วยการพิมพ์จุด แล้วขึ้นบรรทัดใหม่ ผู้เขียนโปรแกรมต้องไม่ลืมที่จะทำการตั้งค่าให้กับ done ใหม่ ให้กลับไปเป็นเท็จก่อนที่จะเริ่มการทำงานในรอบถัดไป
การประยุกต์ใช้งานทางคอมพิวเตอร์ที่สำคัญอย่างหนึ่ง คือ การจำลองระบบการทำงานจริง การวิจัยและพัฒนาที่นำสมัยส่วนใหญ่จำเป็นต้องอาศัยเทคโนโลยีของการจำลอง เพื่อศึกษาว่าระบบทำงานได้อย่างไร โดยปราศจากการทำงานโต้ตอบกับระบบจริงๆ
การจำลองตัวเลขสุ่มเป็นการจำลองการทำงานที่ไม่แน่นอนของระบบในโลกแห่งความจริง และแน่ นอนว่าคอมพิวเตอร์ไม่สามารถที่จะสร้างตัวเลขสุ่มที่แท้จริงออกมาได้ เนื่องจาก การทำงานของคอมพิว เตอร์มีลักษณะแบบกำหนดแน่นอน (deterministic) กล่าวคือ สำหรับคอมพิวเตอร์หนึ่งๆ ถ้าผู้ใช้ป้อนข้อมูลชุดเดิมเข้าไปประมวลผล ก็จะได้ผลลัพธ์ชุดเดิมออกมาเสมอ ดังนั้น การสร้างตัวเลขสุ่มในคอมพิวเตอร์จึงเป็นการสร้างตัวเลขสุ่มแบบเสมือน ให้เกิดชุดตัวเลขที่มีการกระจายที่หลากหลาย ซึ่งตัวเลขเหล่านี้ เรียกว่า ตัวเลขสุ่มเทียม (pseudo-random numbers)
ไฟล์ส่วนหัว <cstdlib> ในภาษา C มาตรฐาน นิยามฟังก์ชัน rand ( ) ในการสร้างจำนวนเต็มสุ่มเทียมในช่วงจาก 0 ถึงค่า RAND_MAX ซึ่งเป็นค่าคงที่ที่ถูกนิยามไว้ในไฟล์ส่วนหัว <cstdlib> โดยที่ ในแต่ละครั้งที่เรียกฟังก์ชัน rand ( ) โปรแกรมจะสร้างจำนวนเต็มแบบ unsigned ในช่วงดังกล่าวให้ 1 ค่า
ตัวอย่างที่ 4.26 การสร้างจำนวนตัวเลขสุ่มเทียม
#include <cstdlib> //defines the rand()and RAND_MAX #include <iostream> int main() { // prints pseudo-random numbers: for (int i=0; i<8; i++) cout << rand() << endl; cout << "RAND_MAX= " << RAND_MAX << endl; }
ทดสอบโปรแกรม ครั้งที่ 1 | ทดสอบโปรแกรม ครั้งที่ 2 |
1103527590 377401575 662824084 1147902781 2035015474 368800899 150802952 486256185 RAND_MAX = 2147483647 | 1103527590 377401575 662824084 1147902781 2035015474 368800899 150802952 486256185 RAND_MAX = 2147483647 |
โปรแกรมนี้ใช้ฟังก์ชัน rand ( ) ในการสร้างจำนวนตัวเลขสุ่มเทียม ในแต่ละครั้งของการประมวลผล คอมพิวเตอร์จะสร้างจำนวนเต็มแบบ unsigned 8 ตัว ซึ่งได้จากการกระจายที่มีรูปแบบเดียวกันในช่วง 0 ถึง RAND_MAX ซึ่งมีค่าเท่ากับ 2,147,483,647 บนคอมพิวเตอร์เครื่องนี้ อย่างไรก็ดี ในแต่ละครั้งของการประมวลผล จะได้ผลรันชุดเดิมเสมอ ทั้งนี้เนื่องจาก มันถูกสร้างมาจากเมล็ด (seed) เดียวกัน
ตัวเลขสุ่มเทียมแต่ละตัวจะถูกสร้างจากตัวเลขสุ่มเทียมตัวก่อนหน้า โดยทำการปรับเปลี่ยนค่าเดิมด้วยวิธีพิเศษแบบ “number crunching” ที่ถูกนิยามภายใน ค่าตัวเลขสุ่มเทียมตัวแรกจะถูกสร้างจากตัวแปรที่ถูกนิยามไว้ในชื่อ seed ซึ่งโดยปกติค่า seed จะถูกนิยามเป็นค่าเฉพาะเดิมๆ ทุกครั้งเมื่อเริ่มการประมวลผล ดังนั้น ผู้เขียนโปรแกรมสามารถใช้ฟังก์ชัน srand ( ) ในการตั้งค่า seed ขึ้นมาเองได้ เพื่อให้ได้มาซึ่งตัวเลขสุ่มเทียมที่ใกล้ค่าสุ่มจริงมากที่สุด
ตัวอย่างที่ 4.27 การตั้งค่า seed แบบโต้ตอบได้ (interactively)
int main() { // prints pseudo-random numbers: unsigned seed; cout << "Enter seed: "; cin >> seed; srand(seed); // initialize the seed for (int i=0; i<8; i++) cout << rand() << endl; }
ทดสอบโปรแกรม เมื่อป้อนค่า seed = 0 | ทดสอบโปรแกรม เมื่อป้อนค่า seed = 1 | ทดสอบโปรแกรม เมื่อป้อนค่า seed = 12345 |
Enter seed: 0 12345 1406932606 654583775 1449466924 229283573 1109335178 1051550459 1293799192 | Enter seed: 1 1103527590 377401575 662824084 1147902781 2035015474 368800899 150802952 486256185 | Enter seed: 12345 1406932606 654583775 1449466924 229283573 1109335178 1051550459 1293799192 794471793 |
โปรแกรมนี้เหมือนกับในตัวอย่างที่ 4.26 เพียงแต่ว่า ในโปรแกรมนี้ตัวสร้างจำนวนตัวเลขสุ่มเทียมสามารถตั้งค่า seed แบบโต้ตอบได้ โดยให้ผู้ใช้เป็นผู้ป้อนค่า ในบรรทัด srand (seed) เป็นการให้ค่าตัวแปร seed แก่ค่า “seed” ภายในใหม่ เพื่อนำไปใช้เป็นค่าเริ่มต้นในฟังก์ชัน rand ( ) เพื่อใช้ในการสร้างตัวเลขสุ่มเทียม ค่า seed ที่แตกต่างกันจะทำให้ได้มาซึ่งผลลัพธ์ที่แตกต่างกัน
ให้สังเกตว่า เมื่อให้ค่า seed มีค่าเป็น 12345 (ค่าตัวเลขสุ่มตัวแรกที่ได้จากการประมวลผลในครั้งแรก) ในการประมวลผลครั้งที่สาม ตัวเลขแรกที่ถูกสร้างขึ้นโดยฟังก์ชัน rand ( ) คือ ค่า 1406932606 ซึ่งเป็นค่าที่สองของชุดตัวเลขสุ่มในการประมวลผลครั้งแรกนั่นเอง ทำให้ชุดตัวเลขสุ่มลำดับที่ 1 ถึง 7 ของการประมวลผลครั้งที่สาม มีค่าเหมือนลำดับที่ 2 ถึง 8 ของการประมวลผลครั้งแรก นอกจากนี้ ผลที่ได้จากการประมวลผลในครั้งที่สอง มีค่าเหมือนกับตัวอย่างที่ 4.26 นั่นหมายความว่าค่า seed ตามปกติของเครื่องคอมพิวเตอร์เครื่องนี้มีค่าเท่ากับ 1
อย่างไรก็ดี แม้ว่าโปรแกรมตัวอย่าง 4.27 จะสามารถสร้างชุดตัวเลขสุ่มที่แตกต่างกันได้ตามค่าที่ผู้ใช้ป้อน ผู้ใช้โปรแกรมก็ยังสามารถคาดเดาคำตอบได้จากค่าที่ตัวเองป้อนเข้าสู่เครื่อง ซึ่งขัดกับธรรมชาติของตัวเลขสุ่ม ที่ไม่ควรคาดเดาได้ว่าจะเป็นตัวเลขใด ปัญหาดังกล่าวสามารถแก้ไขได้โดยใช้นาฬิการะบบในคอมพิวเตอร์ ซึ่งนาฬิการะบบนี้จะเก็บเวลาปัจจุบันในหน่วยวินาที ฟังก์ชัน time ( ) ที่นิยามในไฟล์ส่วนหัว <ctime> จะส่งค่าตัวเลขปัจจุบันในรูปของจำนวนเต็มแบบ unsigned ซึ่งตัวเลขนี้สามารถนำมาใช้เป็นค่า เริ่มต้นให้กับตัวแปร seed เพื่อใช้เป็นค่าเริ่มต้นของฟังก์ชัน rand() อีกที
ตัวอย่างที่ 4.28 การตั้งค่า seed จากนาฬิการะบบ
#include <cstdlib> #include <ctime> // defines the time() function #include <iostream> using namespace std; int main() { // prints pseudo-random numbers: unsigned seed = time(NULL); // uses the system clock cout << "seed = " << seed << endl; srand(seed); // initialize the seed for (int i=0; i<8; i++) cout << rand() << endl; }
ทดสอบโปรแกรม ครั้งที่ 1 | ทดสอบโปรแกรม ครั้งที่ 2 |
Seed = 808148157 1877361330 352899587 1443923328 1857423289 200398846 1379699551 1622702508 715548277 | Seed = 808148160 892939769 1559273790 1468644255 952730860 1322627253 1305580362 844657339 440402904 |
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.27 เพียงแต่ในโปรแกรมนี้ตัวสร้างจำนวนตัวเลขสุ่มเทียมจะได้ค่า seed จากนาฬิการะบบ ในครั้งแรกของการประมวลผล ฟังก์ชัน time ( ) ได้ค่าจำนวนเต็ม 808,148,157 ซึ่งเป็นค่าของนาฬิการะบบในขณะที่ประมวลผล ค่าดังกล่าวจะถูกใช้เป็นค่าเริ่มต้นของตัวแปร seed ในการสร้างตัวเลขสุ่มเทียม การประมวลผลในครั้งที่สอง กระทำในอีก 3 วินาทีต่อมา ทำให้ฟังก์ชั่น time ( ) มีค่าเป็น 808,148,160 ผลที่ตาม โปรแกรมจะสร้างค่าตัวเลขสุ่มเทียมอีกชุด ที่มีค่าแตกต่างกัน โดยที่ผู้ใช้ไม่สามารถบอกได้ล่วงหน้าว่าเป็นเท่าไร ทำให้โปรแกรมในตัวอย่าง 4.28 สามารถใช้สร้างตัวเลขสุ่มเทียม ที่ไม่สามารถบอกได้ว่าเป็นค่าใด ขึ้นกับเวลาที่ประมวลผล ดังนั้น การประมวลผลโปรแกรมแต่ละครั้ง จะได้ชุดตัวเลขสุ่มชุดใหม่เสมอ เช่น
ทดสอบโปรแกรม ครั้งที่ 3 | ทดสอบโปรแกรม ครั้งที่ 4 |
Seed = 943364015 2948 15841 72 25506 30808 29709 13115 2527 | Seed = 943364119 17427 20464 13149 5702 12766 1424 16612 31746 |
โปรแกรมจำลองส่วนใหญ่มักต้องการสร้างตัวเลขสุ่มที่มีค่าแน่นอนในพิสัยข้อมูลที่กำหนด ตัวอย่างต่อไปนี้จะแสดงการปรับค่าตัวเลขสุ่มที่ได้ให้เป็นค่าที่อยู่ในช่วงที่ผู้ใช้ต้องการ
ตัวอย่างที่ 4.29 การสร้างจำนวนตัวเลขสุ่มเทียมในช่วงที่กำหนด
int main() { // prints pseudo-random numbers: unsigned seed = time(NULL); // uses the system clock cout << "seed = " <<seed <<endl; srand(seed); // initialize the seed int min, max; cout << "Enter minimum and maximum: "; cin >> min >> max; // lowest and highest numbers int range = max - min + 1; // number of numbers in range for (int i=0; i<20; i++) { int r = rand()/ 100 % range + min; cout << r << " "; } cout << endl; }
ทดสอบโปรแกรม
Seed = 808237677 Enter minimum and maximum: 1 100 85 57 1 10 5 73 81 43 46 42 17 44 48 9 3 74 41 4 30 68
ทดสอบโปรแกรม
Seed = 808238101 Enter minimum and maximum: 22 66 63 29 56 22 53 57 39 56 43 36 62 30 41 57 26 61 59 26 28
โปรแกรมนี้มีผลการทำงานเหมือนกับตัวอย่างที่ 4.28 ยกเว้นตัวเลขสุ่มเทียมที่ถูกสร้างขึ้นมีขอบเขตในช่วงที่กำหนดไว้ การประมวผลในครั้งแรกสร้างจำนวนเต็ม 20 จำนวน อย่างกระจัดกระจายระหว่างค่าจำนวนเต็ม 1 ถึง 100 ส่วนการประมวลผลในครั้งที่สองสร้างจำนวนเต็ม 20 จำนวน อย่างกระจัดกระจายระหว่างค่าจำนวนเต็ม 22 ถึง 66
ในคำสั่ง for เราหารค่า rand ( ) ด้วย 100 ก่อน เพื่อกำจัดเลขนัยสำคัญสองหลักขวาสุด (หลักสิบและหลักหน่วย) ของตัวเลขสุ่ม เพื่อเป็นการลดปัญหาที่ฟังก์ชันการสร้างตัวเลขสุ่ม จะสร้างตัวเลขสลับกันไปมาระหว่างเลขคู่และเลขคี่ หลังจากนั้น ตัวดำเนินการ % range จะปรับค่าตัวเลขสุ่มให้อยู่ในช่วง 0 ถึง range - 1 และ เมื่อนำค่าที่ได้ไป + min จะทำให้เกิดตัวเลขสุ่มที่อยู่ในช่วงระหว่าง min ถึง max