جلسه چهارم

«تكرار»

مقدمه

تكرار، اجراي پي در پي يك دستور يا بلوكي از دستورالعمل‌ها در يك برنامه است. با استفاده از تکرار مي‌توانيم کنترل برنامه را مجبور کنيم تا به خطوط قبلي برگردد و آن‌ها را دوباره اجرا نمايد.

C++ داراي سه دستور تكرار است: دستور while، دستور do_while و دستور for. دستور‌هاي تکرار به علت طبيعت چرخه‌مانندشان‌، حلقه نيز ناميده مي‌شوند. 

1- دستور while

نحو دستور while به شکل زير است:

while (condition) statement;

به جاي condition، يك شرط قرار مي‌گيرد و به جاي statement دستوري که بايد تکرار شود قرار مي‌گيرد. اگر مقدار شرط، صفر(يعني نادرست) باشد، statement ناديده گرفته مي‌شود و برنامه به اولين دستور بعد از while پرش مي‌كند. اگر مقدار شرط ناصفر(يعني‌ درست) باشد، statement اجرا ‌شده و دوباره مقدار شرط بررسي مي‌شود. اين تکرار آن قدر ادامه مي‌يابد تا اين که مقدار شرط صفر شود.

مثال 1-4 محاسبۀ حاصل جمع اعداد صحيح متوالي با حلقۀ while

اين برنامه مقدار 1 + 2 + 3 + … + n را براي عدد ورودي 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;

}

 

2- خاتمه دادن به يك حلقه

قبلا‌ ديديم كه چگونه دستور break براي كنترل دستورالعمل switch استفاده مي‌شود (به مثال 17-4 نگاه كنيد). از دستور 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;

}

يكي از‌ مزيت‌هاي دستور break اين است كه فورا حلقه را خاتمه مي‌دهد بدون اين که مابقي دستورهاي درون حلقه اجرا شوند.

* مثال‌ 4-4 اعداد فيبوناچي

اعداد فيبوناچي F0, F1, F2, F3, … به شکل بازگشتي توسط معادله‌هاي زير تعريف مي‌شوند:

F0 = 0 , F1 = 1 , Fn = Fn-1 + Fn-2

مثلا براي n=2 داريم:

F2 = F2-1 + F2-2 = F1 + F0 = 0 + 1 = 1

يا براي n=3 داريم:

F3 = F3-1 + F3-2 = F2 + F1 = 1 + 1 = 2

و براي n=4 داريم:

F4 = F4-1 + F4-2 = F3 + F2 = 2 + 1 = 3

 

برنامۀ زير، همۀ اعداد فيبوناچي را تا يك محدودۀ مشخص که از ورودي دريافت مي‌شود، محاسبه و چاپ مي‌كند:

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) break; 

 cout << ", " << f2;

 f0 = f1;

 f1 = f2;}

}

 

 

Enter a positive integer: 1000

Fibonacci numbers < 1000:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987

مثال5-4 استفاده از تابع exit(0)

تابع exit(0) روش ديگري براي خاتمه دادن به يك حلقه‌ است. هرچند که اين تابع بلافاصله اجراي کل برنامه را پايان مي‌دهد:

 

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); 

 cout << ", " << f2;

 f0 = f1;

 f1 = f2; }

}

برنامه‌نويسان ترجيح مي‌دهند از break براي خاتمه دادن به حلقه‌هاي نامتناهي استفاده کنند زيرا قابليت انعطاف بيشتري دارد.

متوقف کردن يك حلقۀ نامتناهي :

 

 با فشردن کليدهاي Ctrl+C سيستم عامل يک برنامه را به اجبار خاتمه مي‌دهد. كليد Ctrl را پايين نگه داشته و كليد C روي صفحه‌كليد خود را فشار دهيد تا برنامۀ فعلي خاتمه پيدا کند.

 

3- دستور do..while

ساختار do..while روش ديگري براي ساختن حلقه است. نحو آن به صورت زير است:

do statement while (condition);

 

به جاي condition يك شرط قرار مي‌گيرد و به جاي statement‌ دستور يا بلوکي قرار مي‌گيرد که قرار است تکرار شود.

اين دستور ابتدا statement‌ را اجرا مي‌كند و سپس شرط condition را بررسي مي‌كند. اگر شرط درست بود حلقه دوباره تکرار مي‌شود وگرنه حلقه پايان مييابد.

 

دستور‌ do..while مانند دستور while است. با اين فرق كه شرط کنترل حلقه به جاي اين که در ابتداي حلقه ارزيابي گردد، در انتهاي حلقه ارزيابي ميشود.

يعني هر متغير كنترلي به جاي اين كه قبل از شروع حلقه تنظيم شود، مي‌تواند درون آن تنظيم گردد.

نتيجۀ ديگر اين است كه حلقۀ do..while هميشه بدون توجه به مقدار شرط كنترل، لااقل يك بار اجرا مي‌شود اما حلقۀ while مي‌تواند اصلا اجرا نشود.

 

مثال 7-4 محاسبۀ حاصل جمع اعداد صحيح ‌متوالي با حلقۀ do..while‌

اين برنامه همان تأثير مثال 1-5 را دارد:

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;

}

 

* مثال 8-4 اعداد فاكتوريال

اعداد فاكتوريال 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

 

برنامۀ زير همۀ‌ اعداد فاكتوريال را که از عدد داده شده کوچک‌ترند، چاپ ميکند:

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);

}

 

4 - دستور for

نحو دستورالعمل for به صورت زير است:

for (initialization; condition; update) statement;

سه قسمت داخل پرانتز، حلقه را کنترل مي‌کنند.

 

عبارت initialization براي اعلان يا مقداردهي اوليه به متغير کنترل حلقه استفاده مي‌شود.اين عبارت اولين عبارتي است که ارزيابي مي‌شود پيش از اين که نوبت به تکرارها برسد. 

 

عبارت condition براي تعيين اين که آيا حلقه بايد تکرار شود يا خير به کار مي‌رود. يعني اين عبارت، شرط کنترل حلقه است. اگر اين شرط درست باشد دستور statement اجرا مي‌شود.

 

عبارت updateبراي پيش‌بردن متغير کنترل حلقه به کار مي‌رود. اين عبارت پس از اجراي statement ارزيابي مي‌گردد.

 

بنابراين زنجيرۀ وقايعي که تکرار را ايجاد مي‌کنند عبارتند از:

 

1 – ارزيابي عبارت initialization

2 – بررسي شرط condition . اگر نادرست  باشد، حلقه خاتمه مي‌يابد.

3 – اجراي statement

4 – ارزيابي عبارت update

5 – تکرار گام‌هاي 2 تا 4

عبارت‌هاي initialization و condition  و updateعبارت‌هاي اختياري هستند. يعني مي‌توانيم آن‌ها را در حلقه ذکر نکنيم.

مثال 9-4 استفاده از حلقۀ for براي محاسبۀ مجموع اعداد صحيح متوالي

اين برنامه همان تأثير مثال 1-5 را دارد:

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;

}

 

در C++ استاندارد وقتي يك متغير كنترل درون يك حلقۀ for اعلان مي‌شود (مانند i در مثال بالا) حوزۀ آن متغير به همان حلقۀ for محدود ميگردد. يعني آن متغير نمي‌تواند بيرون از آن حلقه استفاده شود.

 نتيجۀ ديگر اين است که مي‌توان از نام مشابهي در خارج از حلقۀ for براي يك متغير ديگر استفاده نمود.

 

مثال 12-4 يك حلقۀ for نزولي

برنامۀ زير‌ ده عدد صحيح مثبت را به ترتيب نزولي چاپ مي‌كند:

int main()

{ for (int i=10; i > 0; i--)

 cout << " " << i;

}

مثال 15-4 بيشتر از يك متغير كنترل در حلقۀ for

حلقۀ for در برنامۀ زير دو متغير كنترل دارد:

 

 

int main()

{ for (int m=95, n=11, m%n > 0; m -= 3, n++)

 cout << m << "%" << n << " = " << m%n << endl;

}

 

مثال 16-4 حلقه‌هاي for تودرتو

برنامۀ زير يك جدول ضرب چاپ مي‌كند:

#include  

#include  

int main()

{ for (int x=1; x <= 10; x++)

 { for (int y=1; y <= 10; y++)

 cout << setw(4) << x*y;

 cout << endl;

 }

}

5- دستور break

دستور break يک دستور آشناست. قبلا از آن براي خاتمه دادن به دستور switch و همچنين حلقه‌هاي while و do..while استفاده کرده‌ايم. از اين دستور براي خاتمه دادن به حلقۀ for نيز مي‌توانيم استفاده کنيم.

وقتي دستور break درون حلقه‌هاي تودرتو استفاده شود، فقط روي حلقه‌اي که مستقيما درون آن قرار گرفته تاثير مي‌گذارد.

حلقه‌هاي بيروني بدون هيچ تغييري ادامه مي‌يابند.

 

دستور break در هر جايي درون حلقه مي‌تواند جا بگيرد و در همان جا حلقه را خاتمه دهد.

 

6- دستور continue

دستور break بقيۀ دستورهاي درون بلوك حلقه را ناديده گرفته و به اولين ‌‌دستور بيرون حلقه پرش مي‌كند. دستور continue نيز شبيه همين است اما به جاي اين که حلقه را خاتمه دهد، اجرا را به تكرار بعدي حلقه منتقل مي‌كند.

اين دستور، ادامۀ چرخۀ فعلي را لغو کرده و اجراي دور بعدي حلقه را آغاز ميکند.

مثال 19-4 استفاده از دستورهاي break و continue

اين برنامۀ كوچك،‌ دستورهاي break و continue را شرح مي‌دهد:

int main()

{ int n = 1;

 char c;

 for( ; ;n++ )

 { cout << "\nLoop no: " << n << endl;

 cout << "Continue? ";

 cin >> c;

 if (c = = 'y') continue;

 break;

 }

 cout << "\nTotal of loops: " << n;

}

 

7- دستور goto

 دستورgoto نوع ديگري از دستورهاي پرش است. مقصد اين پرش توسط يك برچسب معين مي‌شود.

 برچسب شناسه‌اي است كه جلوي آن علامت كولن( : ) مي‌آيد و جلوي يك دستور ديگر قرار مي‌گيرد.

 يک مزيت دستور goto اين است که با استفاده از آن مي‌توان از همۀ حلقه‌هاي تودرتو خارج شد و به مکان دلخواهي در برنامه پرش نمود.

مثال 20-4 استفاده از دستور goto براي خارج شدن از حلقه‌هاي تودرتو

int main()

{ const int N=5;

  for (int i=0; i

 { for (int j=0; j

 { for (int k=0; k

 if (i+j+k>N) goto esc;

 else cout << i+j+k << " ";

 cout << "* ";

 }

 esc: cout << "." << endl; 

 }

}

8- توليد اعداد شبه تصادفي

يكي از كاربردهاي بسيار مهم رايانه‌ها، «شبيه‌سازي»‌ سيستم‌هاي دنياي واقعي است. تحقيقات و توسعه‌هاي بسيار پيشرفته به اين راهکار خيلي وابسته است. به وسيلۀ شبيه‌سازي مي‌توانيم رفتار سيستم‌هاي مختلف را مطالعه کنيم بدون اين که لازم باشد واقعا آن‌ها را پياده‌سازي نماييم. در شبيه‌سازي نياز است «اعداد تصادفي» توسط رايانه‌ها توليد شود تا نادانسته‌هاي دنياي واقعي مدل‌سازي شود.

 

رايانه‌ها «ثابت‌کار» هستند يعني با دادن داده‌هاي مشابه به رايانه‌هاي مشابه، هميشه خروجي يکسان توليد مي‌شود. با وجود اين مي‌توان اعدادي توليد کرد که به ظاهر تصادفي هستند؛ اعدادي که به طور يکنواخت در يک محدودۀ خاص گسترده‌اند و براي هيچ‌کدام الگوي مشخصي وجود ندارد. چنين اعدادي را «اعداد شبه‌تصادفي» مي‌ناميم.

   مثال 22-4 توليد اعداد شبه تصادفي

اين برنامه از تابع rand() براي توليد اعداد شبه‌تصادفي استفاده مي‌كند:

#include//defines the rand() and RAND_MAX

#include

int main()

{ // prints pseudo-random numbers:

 for (int i = 0; i < 8; i++)

 cout << rand() << endl;

 cout << "RAND_MAX = " << RAND_MAX << endl;

}

هر بار که برنامۀ بالا اجرا شود، رايانه هشت عدد صحيح unsigned‌ توليد مي‌کند که به طور يکنواخت‌ در فاصلۀ 0 تا RAND_MAX گسترده شده‌اند.‌ RAND_MAX در اين رايانه برابر با 2,147,483,647 است.

هر عدد شبه‌تصادفي از روي عدد قبلي خود ساخته مي‌شود.

 اولين عدد شبه‌تصادفي از روي يك مقدار داخلي که «هسته» گفته مي‌شود ايجاد مي‌گردد.

 هر دفعه که برنامه اجرا شود، هسته با يک مقدار پيش‌فرض بارگذاري مي‌شود.

 براي حذف اين اثر نامطلوب که از تصادفي بودن اعداد مي‌کاهد، مي‌توانيم با استفاده از تابع ()srand خودمان مقدار هسته را انتخاب کنيم.

 

مثال 23-4 كارگذاري هسته به طور محاوره‌اي

اين برنامه مانند برنامۀ مثال 22-4 است بجز اين كه مي‌توان هستۀ توليدکنندۀ اعداد تصادفي را به شکل محاوره‌اي وارد نمود:

 

#include  // defines the rand() and srand()

#include

int main()

{ // prints pseudo-random numbers:

 unsigned seed;

 cout << "Enter seed: ";

 cin >> seed;

 srand(seed); // initializes the seed

 for (int i = 0; i < 8; i++)

 cout << rand() << endl;

}