یکی از ابزارهای پر استفاده و مهم هر زبان برنامه نویسی حلقه های تکرار هستند. وجود چنین ابزاری به برنامه نویس این امکان را می دهد که ساختارهای نیازمند به تکرار مجموعه دستورات (مانند جستجو، گزارش گیری، محاسبات، دریافت اطلاعات از کاربر یا فایل) را پیاده سازی کند.

هر زبانی عموما شامل چندین نوع حلقه تکرار است، که هر کدام به نحوی به برنامه نویس در نوشتن کدهای مختصر و با مفهوم کمک می کنند. در این فرصت با انواع حلقه های تکرار در زبان برنامه نویسی ++C آشنا می شویم.

**
حلقه تکرار while**

این نوع حلقه، ساده ترین نوع حلقه تکرار در این زبان برنامه نویسی است. فرم کلی حلقه while به این صورت است:

while( شرط اجرای حلقه )

{

  دستورات داخل حلقه

}

عبارت های داخل حلقه تا زمانی که شرط اجرای حلقه صحیح باشد اجرا خواهند شد. به عنوان مثال:

int n = 1;

while( n <= 10 )

{

  cout << n << endl;

  n++;

}

در این قطعه کد، ابتدا متغیر n با عدد یک مقداردهی می شود. سپس شرط n ≤ 10 بررسی می شود، که صحیح است. پس اجرای قطعه کد با دستورات داخل حلقه ادامه پیدا می کند. در این حلقه مقدار n چاپ شده و یک واحد به آن اضافه می شود. سپس کنترل برنامه به ابتدای حلقه باز می گردد. اگر شرط حلقه همچنان صحیح باشد، عبارت های داخل آن مجددا اجرا خواهند شد. در نتیجه قطعه کد فوق اعداد یک تا ده را به ترتیب در سطرهای جداگانه خروجی چاپ خواهد کرد.

تذکر: شرط اجرای حلقه قبل از ورود به آن نیز بررسی می شود. اگر این شرط از همان ابتدا نادرست باشد، دستورات داخل حلقه هیچ گاه اجرا نخواهند شد.

int n = 11;

while( n <= 10 )

{

  cout << n << endl;

  n++;

}

این قطعه کد خروجی ندارد. چرا که شرط اجرای حلقه هنگام ورود به آن نیز نادرست است.

حلقه تکرار do-while

فرم کلی این حلقه به صورت زیر است:

do

{

  دستورات داخل حلقه

}while( شرط اجرای حلقه );

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

int n = 1;

do

{

  cout << n << endl;

  n++;

}while( n <= 10 );

در این قطعه کد نیز همانند قطعه کد قبلی اعداد یک تا ده در خروجی چاپ می شوند.

تفاوت این دو حلقه در قطعه کد زیر - که برای حلقه while هم نوشته شده بود - آشکار می شود:

int n = 11;

do

{

  cout << n << endl;

  n++;

}while( n <= 10 );

همانگونه که عنوان شد، در حلقه while شرط اجرای دستورات داخل حلقه در ابتدای آن بررسی می شود. اما در حلقه do-while این شرط در انتهای آن قرار دارد. در نتیجه دستورات داخل حلقه قبل از رسیدن به شرط حلقه یک بار اجرا می شوند. یعنی قطعه کد فوق عدد 11 را چاپ کرده، و سپس با توجه به اینکه شرط n ≤ 10 نادرست است، کنترل برنامه به داخل حلقه باز نمی گردد.

به طور خلاصه می توان گفت: تفاوت حلقه do-while با حلقه while در این است که دستورات داخل حلقه do-while حداقل یک بار اجرا می شوند. بیشتر کاربردهای چنین حلقه ای هم به خاطر همین خاصیت آن است.

**
حلقه for**

ساده ترین نوع تعریف حلقه for به شرح زیر است:

for( نمو ; شرط اجرای حلقه ; مقداردهی اولیه )

{

  دستورات داخل حلقه

}

به مثال زیر توجه کنید:

int n;

for( n = 1 ; n <= 10 ; n++ )

{

  cout << n << endl;

}

این حلقه نیز اعداد یک تا ده را در خروجی چاپ می کند. اما چگونه؟

با اجرای خط اول، متغیر n تعریف می شود. سپس بخش "مقداردهی اولیه" اجرا شده و مقدار n برابر عدد یک می شود. پس از آن "شرط اجرای حلقه" بررسی می شود. این شرط همانند شروط حلقه های قبلی عمل کرده و در صورت نادرست بودن کنترل برنامه از حلقه خارج می شود. اما اگر شرط صحیح باشد کنترل برنامه وارد حلقه شده و دستورات داخل آن اجرا می شوند. در اجرای بعدی بخش "مقداردهی اولیه" اجرا نمی شود. اما قبل از بررسی "شرط اجرای حلقه"، عملیات بخش "نمو" اجرا می شوند. همانگونه که شرح داده شد، این عملیات در اجرای اول و زمان ورود به حلقه اجرا نمی شوند. پس از نمو، شرط اجرای حلقه بررسی شده و به همین ترتیب اجرای برنامه ادامه پیدا می کند.

توجه داشته باشید که لزومی ندارد نمو همواره افزایش یک واحدی باشد:

for( n = 1 ; n <= 10; n += 1 )

for( n = 1 ; n <= 10; n += 2 )

for( n = 10 ; n >= 1; n -= 1 )

for( n = 10 ; n >= 1; n-- )

for( n = 1 ; n <= 100; n *= 2 )

for( n = 100 ; n >= 1; n /= 10 )

تمامی این عبارت ها صحیح هستند.

چنین ساختاری در اکثر زبان های برنامه نویسی- مانند پاسکال و بیسیک - وجود دارد. بزرگترین ویژگی این روش، کنترل شمارشی حلقه ها است. اکثر کاربردهای این حلقه به حالتی باز می گردد که قرار است مجموعه دستوراتی به تعداد معینی انجام شوند. به عنوان مثال تابع زیر مجموع عناصر یک آرایه از اعداد صحیح را محاسبه می کند:

int sum( int arr[ ], int size)

{

  int i, sum = 0;

  for( i = 0 ; i < size ; i++ )

  {

    sum += arr[ i ];

  }

  return sum;

}

همانطور که می دانید، اندیس آرایه ها در زبان ++C از صفر شروع می شوند. پس اگر تعداد عناصر آن size باشد، اندیس ها از صفر تا size - 1 خواهند بود. حلقه for فوق نیز با شروع از عدد صفر و افزایش یک واحد در هر اجرا، مجموع عناصر اندیس های صفر تا size - 1 را محاسبه می کند.

نکته: تعریف ارائه شده برای حلقه for می تواند با استفاده از حلقه while به صورت زیر شبیه سازی شود:

مقداردهی اولیه

while( شرط اجرای حلقه )

{

  دستورات داخل حلقه

  نمو

}

این شبیه سازی دو حسن دارد. اول اینکه عملکرد حلقه for و ترتیب اجرای بخش های سه گانه آن بهتر مشخص می شود. و دوم، ما را متوجه نکته مهمی می کند: لزومی ندارد بخش های سه گانه حلقه for از عبارت های محاسباتی تشکیل شده باشند. هر کدام از بخش های مقداردهی اولیه و نمو می توانند شامل هر دستور متعارفی از این زبان باشند. شرط اجرای حلقه هم می تواند هرگونه شرطی (نه لزوما محاسباتی) باشد. در ضمن توجه داشته باشید که وارد کردن اطلاعات هر کدام از این بخش ها اختیاری است. به مثال ساده زیر توجه کنید:

int n = 0;

for( ; n < 0 || n > 20 ; )

{

  cout << "Enter a number between 0 and 20: ";

  cin >> n;

}

چنین حلقه ای بدون مقداردهی اولیه و نمو می باشد. در صورتی که کاربر عددی بیرون از بازه صفر و بیست وارد کند، شرط ادامه حلقه صحیح بوده و برنامه مجددا با چاپ پیامی منتظر ورود اطلاعات خواهد ماند. البته حلقه هایی نظیر این حلقه بیشتر با while یا do-while پیاده سازی می شوند.

دستور break

این دستور برای خروج از داخل حلقه استفاده می شود. به عبارت دیگر، زمانی که کنترل برنامه در داخل حلقه به این دستور برسد، از آن حلقه خارج شده و به اولین خط بعد از دستورات حلقه منتقل می شود. مثال ساده ای از کاربرد این دستور را می توان در عملیات جستجوی خطی یافت.

تابع زیر در آرایه n عنصری arr (با عناصر متمایز) به دنبال خانه ای با محتوای x می گردد، و شماره اندیس این خانه را به عنوان نتیجه باز می گرداند:

int search( int[ ] arr, int n, int x )

{

  int i, result = -1;

  for( i = 0 ; i < n ; i++ )

  {

    if( arr[ i ] == x )

    {

      result = i;

    }

  }

  return result;

}

روش کار تابع ساده است: با استفاده از حلقه تکرار for اندیس های صفر تا n - 1 برای یافتن عنصر مورد نظر پیمایش می شوند. اگر چنین عنصری یافت شود شماره اندیس خانه مربوطه در result قرار می گیرد. اگر یافت نشود، مقدار result همان منفی یک باقی می ماند که در تعریف آن مقداردهی شده است. چنین عددی هرگز نمی تواند شماره اندیس یک خانه آرایه در زبان ++C باشد. بنابراین روش خوبی برای نشان دادن عدم موفقیت عملیات جستجو است.

این تابع جستجو از لحاظ الگوریتمی ایرادی ندارد. اما می توان در پیاده سازی آن کمی دقیق تر بود. فرض کنید n برابر صد هزار بوده و عدد x در خانه دوم آرایه قرار داشته باشد. پس در تکرار دوم حلقه، عنصر مورد نظر یافت می شود. اما کنترل برنامه تا پایان یافتن بررسی تمامی صد هزار عنصر در داخل حلقه خواهد ماند. این بررسی کاملا بیهوده بوده و موجب اتلاف وقت خواهد شد. این مشکل را می توان با دستور break حل کرد:

int search( int[ ] arr, int n, int x )

{

  int i, result = -1;

  for( i = 0 ; i < n ; i++ )

  {

    if( arr[ i ] == x )

    {

      result = i;

      break;

    }

  }

  return result;

}

تفاوت این تابع با تابع قبلی تنها در دستور break است. با استفاده از این دستور، هرگاه شرط arr[ i ] == x صحیح باشد، پس از تخصیص مقدار i به result، دستور break اجرا می شود. اجرای این دستور موجب می شود که کنترل برنامه از حلقه خارج شده و به اولین خط بعد از آن منتقل شود. بنابراین اگر عنصر مورد نظر ما در خانه شماره دو قرار داشته باشد، دستورات داخل حلقه تنها سه بار (شماره ها از صفر شروع می شوند) تکرار می شوند.

دستور continue

این دستور برای ادامه کار حلقه از تکرار بعدی آن استفاده می شود. به عبارت دیگر، زمانی که کنترل برنامه در داخل حلقه به دستور continue می رسد، از تمامی دستورات بعدی حلقه تا انتهای آن صرف نظر شده و به شروع تکرار بعدی حلقه می رسد. اگر حلقه  مورد نظر، حلقه while یا do-while باشد، شرط ادامه حلقه بررسی می شود؛ اما اگر حلقه for باشد، ابتدا بخش نمو اجرا شده و سپس شرط حلقه بررسی می شود. به مثال ساده زیر توجه کنید:

int i, s = 0, p = 1;

for( i = 1 ; i <= 10 ; i++ )

{

  if( i % 2 == 0 )

  {

    continue;

  }

  s += i;

  p *= i;

}

در این حلقه اگر i عدد زوجی باشد، دستور continue اجرا شده و کنترل اجرای برنامه به ابتدای تکرار بعدی می رود. در نتیجه به مقدار i یک واحد افزوده شده و سپس شرط ادامه حلقه بررسی می شود.

همانطور که از تعریف این دستور پیدا است، کاربرد آن زمانی است که قصد داریم در شرایط خاصی قسمتی از دستورات انتهایی حلقه اجرا نشود.

حلقه هایی با شرط همواره صحیح

تمام حلقه های تکرار برای ادامه کار خود شرطی را بررسی می کنند. این شرط می تواند یک عبارت مقایسه ای ساده، ترکیب عبارات محاسباتی یا هر ساختار دیگری باشد. اگر این شرط به گونه ای باشد که همیشه صحیح باشد، حلقه هایی با شرط همواره صحیح به وجود می آیند. در این حلقه ها شرط ادامه حلقه همواره صحیح است. پس در حالت عادی حلقه هرگز خاتمه پیدا نمی کند! چنین حلقه هایی اگر به درستی کنترل نشوند به یک حلقه بدون توقف تبدیل می شوند که در اصطلاح به آنها حلقه بی نهایت گفته می شود. حلقه های بی نهایت گاهی در اثر اشتباهات منطقی در پیاده سازی الگوریتم بروز می دهند و باعث شکست اجرای صحیج برنامه می شوند. اما اگر در داخل حلقه و در جای مناسب از دستور break استفاده شود، چنین حلقه هایی نیز پایان پذیر خواهند بود:

int n;

while( 1 )

{

  cout << "Enter a positive number:";

  cin >> n;

  if( n > 0 )

  {

    break;

  }

}

شرط اجرای این حلقه عدد یک است. در زبان ++C هر عدد غیر صفر (صحیح یا اعشاری) معنی درست، و عدد صفر معنی نادرست می دهد. پس حلقه فوق یک حلقه با شرط صحیح است. در داخل حلقه از کاربر عدد مثبتی درخواست می شود. اگر کاربر صفر یا یک عدد منفی وارد کند شرط n > 0 صحیح نبوده و کنترل برنامه مجددا به ابتدای حلقه منتقل خواهد شد، تا عدد دیگری از کاربر دریافت کند. اما اگر شرط n > 0 صحیح باشد، دستور break اجرا شده و کنترل برنامه از حلقه خارج خواهد شد. یعنی تکرار حلقه تا زمانی ادامه پیدا می کند که کاربر عدد مثبتی وارد نکرده است. به محض اینکه اولین عدد مثبت وارد شد، کنترل برنامه نیز از حلقه خارج می شود. البته در این مثال خاص می توان حلقه فوق را به صورت زیر نیز پیاده سازی کرد:

int n;

do

{

  cout << "Enter a positive number:";

  cin >> n;

}while( n <= 0 );

 

اگر برای حلقه for شرطی را قائل نشویم، به مفهوم شرط همواره درست است:

 

int n;

for ( ; ; )

{

  cout << "Enter a positive number:";

  cin >> n;

  if( n > 0 )

  {

    break;

  } 

}

توجه: علامت های سمیکالن (;) همیشه باید نوشته شوند.

حلقه های تو در تو

در زبان برنامه نویسی ++C امکان استفاده از حلقه های تکرار تو در تو نیز وجود دارد. قطعه کد زیر جدول ضرب اعداد 4 تا 9 را در خروجی چاپ می کند:

int i, j;

for( i = 4 ; i < 10 ; i++ )

{

  for( j = 4 ; j < 10 ; j++ )

  {

    cout << i * j << " ";

  }

  cout << endl;

}

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

در پایان به دو نکته توجه داشته باشید:

1- در این ساختارها اگر مجموعه دستورات داخل حلقه تنها شامل یک دستور باشد، نیازی به استفاده از آکولاد برای مشخص کردن بلوک مجموعه دستورات داخل حلقه وجود ندارد.

2- عبارت های دستوری break ،while ،do ،for و continue جزو کلمات کلیدی زبان برنامه نویسی ++C هستند.

aachp.ir