آموزش جاوا، فصل هشتم: الگوریتم‌ها و ساختارهای کنترلی در جاوا

۱- الگوریتم چیست؟ یکی از شیوه‌های تفکر سازمان‌یافته (یا ساخت یافته) برای حل مسائل استفاده از الگوریتم است. احتمالاً می‌دانید که کلمه الگوریتم از نام ریاضیدان و منجم بزرگ ایران در قرن دوم هجری،ابوجعفر محمد بن موسی الخوارزمی، گرفته شده است.

هر مسأله‌ای را می‌توان با اجرای مجموعه‌ای از «فعالیت‌ها» به یک «ترتیب» مشخص حل کرد. به هر «روالی» برای حل مسائل که از
۱- «فعالیت» ها به معنی کارهایی که باید انجام شوند،
۲- «ترتیب» مشخصی برای انجام فعالیت‌ها و
۳- «شرط خاتمه» عملیات
ساخته شده باشد، الگوریتم می‌گویند. به عنوان مثالی از یک الگوریتم به مثال زیر دقت کنید:

مسأله: بیدار شدن از خواب و رفتن به سر کار

الگوریتم:
۱- خارج شدن از رختخواب
۲- در آوردن لباس خواب
۳- استحمام کردن
۴- لباس پوشیدن
۵- صبحانه خوردن
۶- خروج از خانه
۷- سوار تاکسی شدن (به مقصد محل کار)
چرا در الگوریتم «ترتیب» اجرای فعالیت‌ها به اندازه خود «فعالیت» ها مهم است؟ فرض کنید در الگوریتم بالا ترتیب اجرای فعالیت‌‌ها را اندکی تغییر دهیم و ترتیب اجرای فعالیت‌های ۳ و ۴ را جا به جا کنیم:

۳- لباس پوشیدن
۴- استحمام کردن

به سادگی می‌توانید حدس بزنید که چه فاجعه‌ای رخ می‌دهد!

در برنامه نویسی هم از الگوریتم استفاده می‌شود.

۲- ساختارهای کنترلی: برنامه‌های رایانه‌ای نوشته شده با هر زبان برنامه نویسی -از جمله جاوا- از سه جزء سازنده اصلی تشکیل می‌شوند که اجرای برنامه را کنترل می‌کنند:
۱- توالی (Sequence)

۲- انتخاب (Selection)

۳- تکرار (Repetition)

۲-۱- توالی (Sequence): توالی به معنی اجرای دستورات برنامه به صورت پشت سر هم و پیاپی است. مثالی که درباره الگوریتم زدیم، یک نمونه از ساختار توالی است. تاکنون تمام مثال‌‌هایی که در فصل‌های قبلی کتاب آورده‌ایم همه از ساختار توالی پیروی می‌کنند. به مثال زیر دقت کنید:

int a = 10;
int b = 10;
int c = a + b;
System.out.println( "a + b = " + c );
int d = a * b;
System.out.println( "a * b = " + d );
...

ترتیب اجرای برنامه در این مثال خط به خط است. ابتدا در خط اول متغیر a تعریف شده و مقداردهی می‌شود. بعد از آن در خط دوم متغیر b تعریف شده و مقداردهی می‌شود. در خط سوم متغیر c تعریف شده و با حاصل جمع a و b مقداردهی می‌شود و … این ساختار توالی نامیده می‌شود.

۲-۲- انتخاب (Selection): گاهی مواقع می‌خواهیم مسیر اجرای برنامه را از بین چند مسیر مختلف انتخاب کنیم. برای مثال می‌خواهیم در صورتی که نمره دانشجو بالاتر از ۱۸ بود به وی پاداش بدهیم و در صورتی که بین ۱۲ تا ۱۴ بود به وی اخطار بدهیم و در صورتی که بین ۱۰ تا ۱۲ بود، وی را جریمه کنیم. همانطور که می‌بینید «فعالیت» مربوط به هر کدام از انتخاب‌های فوق با دیگری متفاوت است. به الگوریتم زیر دقت کنید:

  • اگر نمره دانشجو بالاتر از ۱۸ بود:
    • به دانشجو پاداش بده
  • اگر نمره دانشجو بین ۱۲ تا ۱۴ بود:
    • به دانشجو اخطار بده
  • اگر نمره دانشجو بین ۱۰ تا ۱۲ بود:
    • دانشجو را جریمه کن

جاوا برای ساختار انتخاب از چندین روش مختلف استفاده می‌کند که در این فصل آن‌ها را معرفی خواهیم کرد:

  • ساختار if
  • ساختار if-else
  • ساختار switch

۲-۳- تکرار (Repetition): ساختارهای توالی و انتخاب برای اجرای بسیاری از الگوریتم‌ها کافی است با این حال موارد زیادی پیش می‌آید که می‌خواهیم یک فعالیت را به تعداد زیادی تکرار کنیم. یک کار استفاده از تعداد زیادی دستورات است که به صورت پیاپی (متوالی) اجرا می‌شوند. فرض می‌کنید می‌خواهیم نامه‌های رسیده را پاسخگویی کنیم. الگوریتم این کار به صورت زیر است:
۱- تا زمانی که نامه‌ای وجود دارد:
۱-۱- نامه را پاسخ بده
۲- …
در الگوریتم فوق تا زمانی که نامه‌ای وجود دارد، فعالیت‌های ۱ و ۲ تکرار می‌شوند. این ساختار تکرار نامیده می‌شود.

جاوا برای ساختار تکرار از چندین روش مختلف استفاده می‌کند که در این فصل آن‌ها را معرفی خواهیم کرد:

  • ساختار while
  • ساختار do…while
  • ساختار for
  • ساختار foreach

۳- ساختار تک انتخابی یا if: مواقعی پیش می‌آید که می‌خواهیم در صورت درست بودن یک شرط، یک دستور (Statement) را اجرا کنیم و اگر شرط غلط بود، بدون توجه به دستور، ادامه برنامه را اجرا کنیم. برای مثال می‌خواهیم در صورتی که نمره دانشجو بیشتر از ۱۰ بود، در کارنامه وی جمله «passed» نوشته شود:
۱- اگر نمره دانشجو بیشتر از ۱۰ است
۱-۱- بنویس «Passed»
۲- …
در الگوریتم فوق اگر شرط درست باشد، یعنی نمره دانشجو بیشتر از ۱۰ باشد، دستور داخلی (۱-۱) اجرا می‌شود و بعد از آن دستور ۲ اجرا خواهد شد، ولی اگر شرط غلط باشد، دستور (۱-۱) اجرا نشده و برنامه از دستور ۲ ادامه می‌یابد.

برای انتخاب تکی در جاوا از دستور if استفاده می‌کنیم:

if( condition )
	statement;

در الگوی فوق، condition هر عبارت منطقی است که مقدار بازگشتی آن true یا false باشد. statement نیز می‌تواند هر دستور جاوا باشد. اگر تعداد دستورات داخلی دستور if بیشتر از یکی باشند از شکل زیر برای دستور if استفاده می‌کنیم:

if( condition ) {
	statement1;
	statement2;
	statement3;
	...
}

همانطور که گفتیم، شرط (condition) می‌تواند هر عبارت منطقی با مقدار بازگشتی true یا false باشد. بنابراین می‌توان در دستور if عبارت‌های منطقی را با هم ترکیب کرد:

if( condition1 && ( condition2 || condition3 ) ) {
    statement;
}

چندین مثال از کاربرد دستور if:‌

int a = 5;
int b = 7;
int c = a + b;
if( c > 10 ) {
    System.out.println( "a + b is greater than 10!" );
}

در دستور if فوق، شرط این است که آیا حاصل جمع a + b از ۱۰ بیشتر است یا نه، و از آنجایی که حاصل جمع فوق بیشتر از ۱۰ است، شرط درست است و دستور داخلی if اجرا شده و جمله «a + b is greater than 10!» در خروجی چاپ می‌شود.

int a = 5;
int b = 7;
if( ( a * b ) > 40 ) {
	System.out.println( "are you sure that (a * b) is greater than 40?" );
}

در دستور if بالا، شرط بررسی می‌کند که آیا حاصل ضرب a و b بیشتر از ۴۰ است یا نه، و از آنجایی که مقدار این حاصل ضرب کمتر از ۴۰ است، شرط غلط (false) بوده دستور داخلی if اجرا نخواهد شد.

۴- ساختار دو انتخابی یا if-else: در دستور if اگر شرط درست بود، دستورهای داخلی if اجرا می‌شوند و در غیر این صورت، برنامه از روی این دستورها پرش می‌کند و دستورهای بعد از if را اجرا می‌کند. مواقعی پیش می‌آید که می‌خواهیم در صورت غلط بودن شرط دستور if دستور خاصی را اجرا کنیم. برای مثال به الگوریتم زیر دقت کنید:
اگر نمره دانشجو بیشتر از ۱۰ بود
بنویس «قبول»
در غیر این صورت
بنویس «مردود»

در جاوا در چنین مواردی از دستور if-else استفاده می‌کنیم:

if( condition )
	statement1;
else
	statement2;

در الگوی فوق اگر شرط condition درست باشد، دستور statement1 اجرا خواهد شد. اما اگر شرط condition غلط باشد، دستور statement2 اجرا خواهد شد.

همانطور که پیش از این هم گفتیم اگر تعداد دستورات داخلی دستور if یا else بیشتر از یکی باشد از شکل زیر استفاده می‌کنیم:

if( condition ) {
	statement1;
	statement2;
	statement3;
	...
} else {
	statement1;
	statement2;
	statement3;
	...
}

مثالی از کاربرد دستور if-else: در اینجا کد جاوای همان مثالی را که به عنوان الگوریتم ذکر کردیم، می‌نویسیم:

if( grade > 10 ) {
	System.out.println("Passed");
} else {
	System.out.println("Failed");
}

در این مثال ابتدا شرط دستور if بررسی می‌شود، اگر شرط درست باشد دستور داخلی if اجرا شده و جمله «Passed» در خروجی چاپ می‌شود. اما اگر شرط (grade > 10) درست نباشد، به بیان دیگر اگر grade کمتر از ۱۰ باشد، شرط if غلط می‌شود و بنابراین دستور داخلی else اجرا شده جمله «Failed» در خروجی چاپ خواهد شد.

۵- ترکیب دستور if و else: همانطور که دیدید، الگوی تعریف دستور if-else به شکل زیر است:

if( condition ) {
	statement1;
} else {
	statement2;
}

همانطور که گفتیم دستور statement1 و دستور statement2 می‌توانند هر دستور مجاز جاوا باشند، بنابراین می‌توان در درون دستور if و دستور else مجدداً از دستورات if و else استفاده کرد. به مثال زیر دقت کنید:

int a = 5;
int b = 7;
if( a != 5 ) {
	System.out.println("a != 5");
} else {
	if( b == 7 ) {
		System.out.println("a = 5 and b = 7");
	}
}

قطعه برنامه بالا را به دقت ببینید. همانطور که می‌بینید یک دستور if درون دستور else قرار گرفته است. برای سادگی بیشتر می‌توان آن‌ها را ترکیب کرده و به شکل زیر نوشت:

int a = 5;
int b = 7;
if( a != 5 ) {
	System.out.println("a != 5");
} else if( b == 7 ) {
	System.out.println("a = 5 and b = 7");
}

در مثال بالا ابتدا شرط دستور if بررسی می‌شود. اگر شرط if غلط باشد، آنگاه شرط else if بررسی می‌شود، اگر شرط else if درست باشد، دستورات داخلی else-if اجرا می‌شود و در غیر این صورت اجرای برنامه از اولین دستور بعد از else-if ادامه می‌یابد.

می‌توان ترکیب else-if را بیشتر هم کرد. به مثال زیر توجه کنید:

if( condition1 ) {
	statement1;
} else if( condition2 ) {
	statement2;
} else if( condition3 ) {
	statement3;
} else {
	statement4;
}

در مثال فوق اگر شرط condition1 درست باشد، statement1 اجرا خواهد شد. اگر شرط condition2 درست باشد، statement2 اجرا خواهد شد و … و اگر هیچکدام از شرط‌های فوق درست نباشند، statement4 اجرا خواهد شد. البته آخرین else اختیاری است و فقط در صورتی آن را اضافه می‌کنیم که بخواهیم در صورت غلط بودن کلیه شرط‌ها، دستورات خاصی را اجرا کنیم.

دستور if-else-if ساختار انتخاب چندتایی در جاوا است.

۶- ساختار انتخاب چندتایی با switch: همانطور که در بخش قبلی گفتیم، ساختار انتخاب چندتایی در جاوا دستور if-else-if است. همانطور که دیدید اگر تعداد شرط‌های این دستور زیاد باشد ممکن است پیچیدگی آن از خوانایی برنامه بکاهد. برای حل این مشکل، در جاوا ساختار انتخاب چندتایی switch معرفی شده است. ساختار کلی دستور switch به شکل زیر است:

switch( statement ) {
	case value1:
		statement1;
		break;
	case value2:
		statement2;
		break;
	case value3:
		statement3;
		break;
	...
	default:
		statementN;
		break; //Optional
}

هر زمان که کنترل اجرای برنامه به دستور switch می‌رسد، عبارت داخل پرانتز مقابل switch را ارزیابی می‌کند. ماحصل این عبارت باید یکی از انواع داده اولیه byte یا char یا short یا int باشد. سپس ماحصل این عبارت را با مقادیر مقابل case های دستور مقایسه می‌کند. در صورتی که این دو مقدار با هم برابر باشند، کنترل اجرای برنامه به دستورات داخلی case منتقل شده و تارسیدن به دستور break ادامه می‌یابد. اگر حاصل عبارت switch با هیچکدام از مقادیر case ها برابر نبود، دستورات داخلی default اجرا می‌شود. البته default اختیاری است و می‌توانیم آن را ننویسیم. به مثال زیر دقت کنید:

int a = 5;
int b = 10;
switch( a + b ) {
	case 5:
		System.out.println("a + b = 5 ?");
		break;
	case 10:
		System.out.println("a + b = 10 ?");
		break;
	case 15:
		System.out.println("a + b = 15 ?");
		break;
	default:
		System.out.println("a + b = " + ( a + b ) );
		break; //Optional
}

در این مثال دو متغیر صحیح از نوع int تعریف کرده‌ایم. وقتی اجرای برنامه به دستور switch می‌رسد، حاصل عبارت a + b محاسبه می‌شود و سپس این مقدار با مقادیر مقابل عبارت‌های case مقایسه می‌شود. ابتدا حاصل جمع a + b با ۵ (case اول) مقایسه می‌شود، از آنجایی که این دو عبارت برابر نیستند، این عبارت با ۱۰ (case بعدی) مقایسه می‌شود، ولی باز هم این دو عبارت برابر نیستند، بنابراین حاصل جمع فوق با ۱۵ که مقدار case بعدی است مقایسه می‌شود، چون این دو عبارت با هم برابرند، کنترل اجرای برنامه به دستورات داخل این case منتقل می‌شود. سپس دستورات داخلی این case تا رسیدن به اولین دستور break ادامه می‌یابد.

نکته: اگر دستور break را در انتهای دستورات هر case ننویسیم چه اتفاقی می‌افتد؟ به مثال زیر دقت کنید:

char ch = 'a';
switch( ch ) {
	case 'a':
		System.out.println("ch is a");
	case 'A':
		System.out.println("ch is A");
		break;
	default:
		System.out.println("ch is not a or A");
		break; //Optional
}

در دستور switch بالا، ابتدا نویسه (کاراکتر) ch با عبارت مقابل اولین case مقایسه می‌شود و چون این دو نویسه با هم برابرند، دستور داخل case اجرا شده و عبارت «ch is a» در خروجی چاپ می‌شود ولی بلافاصله دستور داخل case بعدی همم اجرا شده و عبارت «ch is A» در خروجی چاپ می‌شود. در بسیاری از موارد، مثل همین مثال، از قلم انداختن دستور break ممکن است به خطاهای منطقی (خطاهایی که از دید کامپایلر خطا نیستند ولی اجرای برنامه را مختل می‌کنند) در برنامه منجر شود. البته ممکن است از این نکته به صورت هوشمندانه‌ای استفاده کرد. به مثال زیر دقت کنید:

char selection = 'a';
switch( ch ) {
	case 'a':
	case 'A':
		someMethodA();
		break;
	case 'b':
	case 'B':
		someMethodB();
		break;
	default:
		System.out.println("Unknown command.");
		break; //Optional
}

در این مثال، فرض بر این است که کاربر برنامه می‌تواند از بین دو گزینه a و b انتخاب کند. برای این کار کاربر باید یکی از دو حرف a یا b را وارد کند. اما ممکن است کاربر شکل بزرگ این حروف یعنی A یا B را وارد کند. با ترفندی که به کار برده‌ایم، هیچ تفاوتی بین a و A (و همین طور b و B) نیست و اگر کاربر a یا A را وارد کند، متد someMethodA() اجرا می‌شود. از این شیوه در آینده فراوان استفاده خواهید کرد و نمونه‌های زیادی خواهید دید!

۷- ساختار تکرار با while: تاکنون الگوریتم‌های زیادی را دیده‌اید. در اغلب این الگوریتم‌ها نیازی به تکرار برخی دستورات نبوده است. ولی در اغلب مسائلی که می‌خواهیم برای آن‌ها برنامه بنویسیم، به مواردی برمی‌خوریم که نیاز داریم تعدادی از دستورات را چندین بار تکرار کنیم. برای این کار از ساختارهای تکرار (که به آن‌ها حلقه یا لوپ (loop) هم گفته می‌شود) استفاده می‌کنیم.

فرض کنید می‌خواهید مجموع و میانگین ۱۰۰ عدد را محاسبه و در خروجی چاپ کنید. چه می‌کنید؟ آیا ۱۰۰ متغیر تعریف می‌کنید و هر بار مقدار یکی از آن‌ها را از ورودی می‌خوانید؟ یا این که یک بار دستور خواندن از ورودی را می‌نویسید و از برنامه می‌خواهید که آن را برای شما ۱۰۰ بار تکرار کند؟ مسلماً روش دوم را استفاده خواهید کرد. بنیادی‌ترین ساختار تکرار در جاوا حلقه while است. نحو (Syntax) دستور while به صورت زیر است:

while( condition )
	statement;

و همانطور که برای سایر ساختارهای کنترلی دیدید، اگر تعداد دستورات داخلی while زیاد باشد، حتماً از { و } استفاده می‌کنیم:

while( condition ) {
	statement1;
	statement2;
	statement3;
    // ...
}

در ساختار تکرار while تا زمانی که شرط condition درست (true) باشد، دستور یا دستورات داخلی while تکرار خواهند شد. به مثال زیر دقت کنید:

System.out.println( "Start of while Loop!" );
int counter = 0;
while( counter < 10 ) {
	System.out.println( "now counter is: " + counter );
	counter++;
}
System.out.println( "End of while Loop!" );

زمانی که اجرای برنامه به دستور while می‌رسد، مقدار اولیه متغیر counter برابر ۰ است. while بررسی می‌کند تا ببیند که آیا شرط counter < 10 درست (true) است یا خیر. چون این شرط درست است، عبارت داخلی while اجرا می‌شود. در داخل دستور while هر بار یکی به متغیر counter افزوده می‌شود. وقتی که مقدار counter برابر ۱۰ می‌شود، شرط حلقه غلط (false) می‌شود و کنترل اجرای برنامه به اولین دستور بعد از while منتقل می‌شود. خروجی قطعه کد فوق به شکل زیر است:

Start of while Loop!
now counter is: 0
now counter is: 1
now counter is: 2
now counter is: 3
now counter is: 4
now counter is: 5
now counter is: 6
now counter is: 7
now counter is: 8
now counter is: 9
End of while Loop!

نکته: به حلقه‌های تکرار که از یک متغیر به عنوان شمارنده استفاده می‌کنند، حلقه‌های تکرار با شمارنده می‌گویند. در این نوع حلقه‌های تکرار، نیازمند یک متغیر شمارنده به همراه سه ویژگی زیر هستیم:
۱- متغیر شمارنده دارای مقدار اولیه معتبر باشد. فرض کنید در مثال بالا، مقدار اولیه متغیر counter که به عنوان شمارنده حلقه به کار می‌رود، به جای ۰، برابر با ۱۰ بود. در این حالت برنامه به هیچ وجه داخل این حلقه تکرار نمی‌شد!

۲- میزان گام افزایش یا کاهش شمارنده مشخص باشد. در هر بار اجرای حلقه، مقدار شمارنده حلقه باید تغییر کند. اگر این اتفاق نیافتد، شرط حلقه تکرار همواره درست است و ممکن است برنامه در حلقه تکرار بی‌نهایت بیافتد، حلقه تکراری که هیچ گاه از آن خارج نمی‌شود! در مثال بالا، در هر بار اجرای حلقه، به شمارنده یکی افزوده می‌شود.

۳- شرط خاتمه حلقه تکرار، دست‌یافتنی باشد. اگر هیچ گاه شرط کنترل کننده حلقه تکرار، غلط (false) نشود، باز هم مثل مورد بالا ممکن است در حلقه تکرار بی‌نهایت گرفتار شویم. برای مثال فرض کنید در قطعه کد بالایی، به جای این که در هر بار اجرای حلقه، یکی به counter اضافه کنیم، یکی از آن کم می‌کردیم. آنگاه هیچ وقت شرط حلقه تکرار غلط (false) نمی‌شد و همواره این حلقه تکرار می‌شد و دستورات بعد از حلقه تکرار اصلاً اجرا نمی‌شدند!
حلقه تکرار while هیچکدام از سه ویژگی گفته شده در بالا را مستقیماً کنترل نمی‌کند. بنابراین شما باید، متغیر کنترلی خود را بیرون حلقه تعریف کنید و گام افزایش یا کاهش آن را به نحو مقتضی داخل حلقه بیاورید. برای غلبه بر این مشکلات از حلقه تکرار for استفاده می‌کنیم که در بخش‌های بعدی همین فصل آن را توضیح خواهیم داد.

۸- ساختار تکرار با do-while: دستور do-while از نظر مفهومی بسیار شبیه به حلقه تکرار while است. به نحو (Syntax) این دستور دقت کنید:

do
	statement;
while( condition );

اگر تعداد دستورات داخلی do-while بیشتر از یکی باشد، از { و } استفاده می‌کنیم:

do {
	statement1;
	statement1;
	statement1;
	...
} while( condition );

بزرگ‌ترین تفاوت بین حلقه‌های تکرار while و do-while در این است که در حلقه‌‌های while شرط حلقه در ابتدای هر تکرار بررسی می‌شود ولی در حلقه do-while ابتدا یک تکرار انجام شده و سپس شرط حلقه بررسی می‌شود. برای درک بهتر تفاوت این دو حلقه تکرار به مثال‌‌های زیر دقت کنید:

char userSelect;
do {
	userSelect = displayMenu();
	switch( userSelect ) {
		case 'a':
		case 'A':
			doMethodA();
			break;
		case 'b':
		case 'B':
			doMethodB();
			break;
		case 'q':
		case 'Q':
			sayGoodBye();
			break;
	}
} while( userSelect != 'q' && userSelect != 'Q' );

در قطعه کد بالا، فرض بر این است که تابع displayMenu() یک منو را به کاربر نمایش داده و از وی می‌خواهد که برای انجام یک کار، حرف a یا A و برای انجام کار دوم، حرف b یا B را وارد کند و اگر می‌خواهد از برنامه خارج شود، حرف q یا Q را وارد کند. در ابتدای اجرای برنامه، وقتی کنترل اجرای برنامه به دستور do-while می‌رسد، متغیر کنترلی حلقه تکرار، که همان userSelect است، هیچ مقداری ندارد. اما پس از اولین تکرار در حلقه این متغیر دارای مقدار می‌شود، سپس در دستور switch این مقدار بررسی شده و اگر برابر با a یا A باشد متد doMethodA() و اگر برابر b یا B باشد، متد doMethodB() فراخوانی می‌شود. اگر کاربر q یا Q (به معنی خروج از برنامه) را وارد کند، متد sayGoodBye() پیغام خروج را برای وی نمایش می‌دهد. حال اولین تکرار حلقه do-while به اتمام می‌رسد و حالا شرط حلقه تکرار بررسی می‌شود. این شرط این است که متغیر userSelect دارای مقدار q و Q نباشد.

همانطور که دیدید، دستورات داخلی حلقه تکرار do-while حداقل یک بار انجام می‌شوند در حالی که در حلقه while ممکن است این اتفاق نیافتد.

نکته: در انتهای عبارت while در دستور do-while حتماً سمی‌کالن «;» بگذارید.

۹- ساختار تکرار با for: ساختار تکرار for پرکاربردترین ساختار تکرار در زبان جاوا است. در این ساختار تکرار، بر خلاف ساختار while، متغیر شمارنده به همراه مقدار اولیه آن، میزان گام افزایش یا کاهش شمارنده و همچنین شرط خاتمه حلقه تکرار در داخل for تعریف می‌شوند و این باعث می‌شود که حلقه تکرار با for بسیار خواناتر از حلقه‌های تکرار while و do-while باشد. به نحو (Syntax) حلقه for دقت کنید:

for( <Expression1> ; <Expression2> ; <Expression3> )
	statement;

همانطور که می‌بینید در تعریف حلقه تکرار با for سه عبارت در هلال (پرانتز) آمده است. عبارت اول، تعریف متغیر شمارنده و مقداردهی اولیه به آن است. عبارت دوم شرط خاتمه حلقه تکرار و عبارت سوم گام پرش شمارنده حلقه است. علت این که هر کدام از این عبارت‌ها را در <> قرار داده‌ایم، نشان از اختیاری بودن هر کدام از این عبارت‌ها است. به مثال زیر دقت کنید:

for( int i = 0 ; i < 10 ; i++ )
	System.out.println( i );

این حلقه تکرار به زبان ساده این است: «متغیر i را برابر ۰ قرار بده و تا وقتی که i < 10 است، هر بار دستور زیر را اجرا کن و سپس یکی به i اضافه کن».

نکته: گام پرش حلقه تکرار for پس از اجرای دستورات داخلی حلقه تکرار for اعمال می‌شود.

نکته: در صورتی که تعداد دستورات داخلی ساختار تکرار for بیشتر از یکی باشد، از { و } استفاده می‌کنیم:

int sum = 0;
for( int i = 0 ; i < 10 ; i++ ) {
	System.out.println( "i is: " + i );
	sum += i;
	System.out.println( "sum is: " + sum );
}

یکی از ویژگی‌های جالب ساختار تکرار for در جاوا این است که می‌توان در یک حلقه تکرار، از چندین مقدار اولیه و چندین گام پرش مختلف استفاده کرد. به مثال زیر دقت کنید:

for( int i = 0 , j = 10 ; i < 10 ; i++ , j-- ) {
	System.out.println( "( " + i " , " + j + " ) " );
}

پس از اجرای قطعه کد فوق، خروجی زیر را خواهید دید:

( ۰ , ۱۰ )
( ۱ , ۹ )
( ۲ , ۸ )
( ۳ , ۷ )
( ۴ , ۶ )
( ۵ , ۵ )
( ۶ , ۴ )
( ۷ , ۳ )
( ۸ , ۲ )
( ۹ , ۱ )

همانطور که گفتیم، هر کدام از سه عبارتی که در ساختار تکرار for می‌آیند اختیاری هستند. یعنی حلقه تکرار for می‌تواند بدون تعریف متغیر شمارنده و مقداردهی به آن باشد. یا این که حلقه for می‌تواند بدون گام پرش متغیر شمارنده باشد. البته سمی‌کالن‌ها «;» باید حتماً بیایند. به مثال‌های زیر دقت کنید:

int i = 0;
for( ; i < 10 ; i++ ) {
	...
}

// OR 

int i = 0;
for( ; i < 10 ; ) {
	...
	i++;
}

۱۰- حلقه‌های تکرار تو در تو (Nested loop): در هر ساختار تکرار می‌توانیم تعدادی دستور جاوا را اجرا کنیم، ولی آیا امکان دارد که در یک حلقه تکرار، یک حلقه تکرار دیگر داشته باشیم؟ قطعاً بله، البته با دقت و احتیاط! به قطعه کد زیر دقت کنید و حدس بزنید که چه کاری می‌کند:

for( int i = 0 ; i < 10 ; i++ ) {
	for( int j = 0 ; j < 10 ; j++ ) {
		System.out.print( ( i * j ) + "  " );
	}
	System.out.println();
}

اگر حدس زده‌اید که این برنامه جدول ضرب را تولید می‌کند درست حدس زده‌اید! نمونه خروجی برنامه را ببینید:

۱	۲	۳	۴	۵	۶	۷	۸	۹	۱۰
۲	۴	۶	۸	۱۰	۱۲	۱۴	۱۶	۱۸	۲۰
۳	۶	۹	۱۲	۱۵	۱۸	۲۱	۲۴	۲۷	۳۰
۴	۸	۱۲	۱۶	۲۰	۲۴	۲۸	۳۲	۳۶	۴۰
۵	۱۰	۱۵	۲۰	۲۵	۳۰	۳۵	۴۰	۴۵	۵۰
۶	۱۲	۱۸	۲۴	۳۰	۳۶	۴۲	۴۸	۵۴	۶۰
۷	۱۴	۲۱	۲۸	۳۵	۴۲	۴۹	۵۶	۶۳	۷۰
۸	۱۶	۲۴	۳۲	۴۰	۴۸	۵۶	۶۴	۷۲	۸۰
۹	۱۸	۲۷	۳۶	۴۵	۵۴	۶۳	۷۲	۸۱	۹۰
۱۰	۲۰	۳۰	۴۰	۵۰	۶۰	۷۰	۸۰	۹۰	۱۰۰

در برنامه فوق دو حلقه تکرار تو در تو را می‌بینید. در حلقه اول، یک متغیر شمارنده به نام i و در دومی یک متغیر شمارنده به نام j می‌بینید. ابتدا متغیر i با مقدار اولیه ۱ تعریف می‌شود. بعد برنامه وارد حلقه تکرار داخلی می‌شود و متغبر j با مقدار اولیه ۱ تعریف می‌شود. پس از آن حلقه داخلی ۱۰ بار تکرار می‌شود و در هر بار تکرار، یک عدد به خروجی می‌رود. هر بار که تکرار حلقه داخلی تمام شود، در حلقه بیرونی، خروجی به خط بعد منتقل شده و باز مجدداً حلقه داخلی تکرار می‌شود و … دستور داخلی حلقه تکرار داخلی چند بار تکرار می‌شود؟ ۱۰ بار حلقه بیرونی اجرا می‌شود و در هر بار اجرای این حلقه، حلقه داخلی هم ۱۰ بار تکرار می‌شود، یعنی دستور داخلی حلقه تکرار داخلی صد بار اجرا می‌شود.

نکته: حلقه‌های تکرار تو در تو معمولاً برای پردازش آرایه‌های چند بعدی و ساختار داده‌های مشابه به کار می‌رود.

۱۱- دستورهای break و continue: تا حدی با دستور break در دستور switch آشنا شدید. دستورهای switch و continue برای کنترل بیشتر جریان اجرای برنامه در ساختارهای انتخاب و تکرار به کار می‌روند. بسیاری مواقع ممکن است در میانه اجرای یک حلقه تکرار از ادامه اجرای حلقه تکرار منصرف شویم. برای مثال فرض کنید در یک آرایه به دنبال یک عدد خاص می‌گردیم، به محض این که عدد مورد نظر پیدا شد، دیگر نیازی به تکرار بیهوده حلقه تکرار نیست. در این حال چه باید کرد؟ باید از حلقه بیرون آمد. برای خروج از حلقه تکرار می‌توان از دستور break استفاده کرد. به مثال زیر دقت کنید:

int [] list = new int[] { 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 };
for( int i = 0 ; i < list.length ; i++ ) {
	if( list[i] == 20 ) {
		System.out.println("Hooray! we found 20!");
		break;
	}
}

در قطعه برنامه فوق، در ابتدای هر بار اجرای حلقه، بررسی می‌کنیم که آیا عدد مورد نظر برابر ۲۰ است یا نه. اگر عدد مورد نظر برابر ۲۰ باشد، با چاپ یک عبارت، از حلقه تکرار خارج می‌شویم.

کارکرد دستور continue هم به نوعی مشابه است. این بار می‌خواهیم تا انتهای دستورات حلقه پیش نرفته و مجدداً از ابتدای حلقه، دستورات را اجرا کنیم، البته با مقادیر جدید شمارنده! مثال زیر را ببینید:

float [] grades = new float[] { 18 , 9 , 11 , 16 , 6.5f , 14 , 19 , 9 };
for( int i = 0 ; i < grades.length ; i++ ) {
	if( grades[i] < 10 ) {
		continue;
	}
	System.out.println("Passed, grade is: " + grades[i] );
}

در حلقه تکرار فوق، اگر مقدار grades[i] کمتر از ده باشد، بقیه دستورات داخلی حلقه تکرار را ادامه نداده و مجدداً برمی‌گردیم و از ابتدای حلقه مجدداً تکرار می‌کنیم، با این تفاوت که مقدار شمارنده یکی بیشتر شده است. ماحصل این تکرار چاپ نمره‌های بیشتر از ۱۰ است. به خروجی برنامه دقت کنید:

Passed, grade is: 18.0
Passed, grade is: 11.0
Passed, grade is: 16.0
Passed, grade is: 14.0
Passed, grade is: 19.0

نکته: کاربرد دستورات break و continue در حلقه‌های تکرار است. تنها کاربرد break در خارج از ساختارهای تکرار، در دستور switch است.

ادامه دارد …

7 فکر می‌کنند “آموزش جاوا، فصل هشتم: الگوریتم‌ها و ساختارهای کنترلی در جاوا

  1. aL!R3z4

    “با سلام”
    من معمولا برای مطالب سایت ها نظر نمیذارم!
    ولی به خاطر توضیحات بسیار دقیق و قابل فهم این مطلب آموزشی، وظیفه خودم دونستم تا از نویسنده محترم اسمارت لب تشکر ویژه به عمل بیارم…
    چون برای یافتن این آموزش به سایت های دیگری هم سر زدم اما هیچ کدام به درد من نخوردند…
    یا خیلی پیچیده بودند یا بد تعریف شده بودند..!
    امیدوارم موفق و پیروز باشید…
    “با تشکر فراوان”

    پاسخ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *