در این مقاله، به آموزش حلقه ی while در جاوا می پردازیم. و با استفاده از چند مثال، کدنویسی به این روش را آموزش می دهیم.
ما می توانیم از یک حلقه ی while برای اجرای تعدادی دستور به طور مداوم، تا زمانی که عبارت بولین آن true است، استفاده کنیم. این عبارت بولین، که در ورودی حلقه، کنترل می شود، تا زمانی که ارزش آن true باشد، اجرای حلقه ادامه می یابد.
ما می توانیم از یک حلقه ی while، وقتی که می خواهیم مقداری کد را به تعداد دفعات مشخصی تکرار کنیم، استفاده کنیم. یک حلقه که به تعداد مشخصی اجرا می شود، یک حلقه ی محدود(definite) یا شمارشی(counted) نامیده می شود. از طرف دیگر، تعداد دفعاتی که این حلقه اجرا می شود، ممکن است تا زمانی که برنامه در حال اجراست، مشخص نشود. به چنین حلقه ای، حلقه ی نامحدود(indefinite) گفته می شود، زیرا نمی دانیم این حلقه، در نهایت چند بار اجرا می شود.
نوشتن یک حلقه ی while محدود
برای نوشتن یک حلقه ی محدود(definite)، ما یک متغیر که حلقه را کنترل می کند، مقداردهی اولیه می کنیم؛ یک متغیر که مقدار آن مشخص می کند که آیا حلقه ادامه یابد یا خیر. تا هنگامی که این مقدار بولین که از مقایسه ی متغیر کنترل حلقه و یک مقدار دیگر حاصل می شود، true باشد، اجرای بدنه ی حلقه ی while ادامه می یابد. در بدنه ی این حلقه، باید یک دستور اضافه کنیم که متغیر کنترل حلقه را تغییر دهد. بعنوان مثال، قطعه کد نشان داده شده در تصویر 6.2 یک سری عدد صحیح را از 1 تا 10 نمایش می دهد. متغیر val همان متغیر کنترل حلقه است. وقتی که مقدار این متغیر، برابر با 1 است، حلقه شروع می شود و تا هنگامی که این متغیر کمتر از 11 است، مقدار آن در خروجی نشان داده می شود و افزایش می یابد.
(تصویر 6.2 : یک حلقه ی while که اعداد صحیح از 1 تا 10 را نمایش می دهد)
خطاب به دانشجویان جبر: یک عبارت به صورت val = val + 1;
اشتباه به نظر می رسد. زیرا یک مقدار هرگز نمی تواند یک واحد بیشتر از خود باشد؛ در جبر، علامت مساوی، به معنی برابری است. اما در جاوا، علامت مساوی، باعث می شود که یک مقدار در سمت راست به متغیر در سمت چپ، انتساب داده شود. بنابراین در عبارت val = val + 1;
مقدار val گرفته می شود و یک واحد به آن اضافه می شود و آنگاه این مقدار جدید، در داخل متغیر val قرار می گیرد.
وقتی که ما برنامه هایی می نویسیم که حاوی حلقه ها هستند، ممکن است به راحتی اشتباه کنیم. بعنوان مثال، اجرای کد نشان داده شده در تصویر 6.3 باعث می شود (از نظر تئوری) پیغام "Hello" برای همیشه نمایش یابد؛ زیرا هیچ کدی برای خاتمه دادن به حلقه، وجود ندارد. به یک حلقه که هرگز پایان نمی یابد، حلقه ی بی نهایت(infinite) گفته می شود.
(تصویر 6.3 : یک حلقه که به صورت بی نهایت، پیغام Hello را نمایش می دهد)
توضیح عبارت شماره 1 در تصویر بالا: این کار را انجام ندهید! این حلقه هیچگاه پایان نمی یابد؛ زیرا ارزش عبارت تست شده همواره true است.
نکته: یک حلقه ی بی نهایت، در حقیقت، ممکن نیست که به طور بی نهایت اجرا شود. بسته به کاری که این حلقه انجام می دهد، در نهایت ممکن است مموری کامپیوتر خسته شود(به صورت واقعی یا به صورت مجازی) و اجرای حلقه متوقف شود. همچنین این امکان وجود دارد که پردازنده ی کامپیوتر از یک ویژگی وقت استراحت(time-out) برخوردار باشد که با اجبار این حلقه را به پایان برساند. به هر حال، بسته به سیستم شما، زمان زیادی می تواند بگذرد تا این حلقه متوقف شود.
در تصویر 6.3، عبارت 4 > 2
به صورت true ارزیابی می شود. ما به وضوح نیازی به انجام چنین ارزیابی-ای نداریم، اما اگر در این حلقه ی while چنین کاری را انجام دهیم، بدنه ی حلقه وارد اجرا می شود و پیغام "Hello"
نمایش داده می شود. در ادامه، این عبارت (یعنی 4 بزرگتر از 2) دوباره ارزیابی می شود. ارزش عبارت 4 >2
هنوز برابر با true است؛ بنابراین کدهای بدنه ی حلقه دوباره اجرا می شود. پیغام "Hello"
به طور مکرر نمایش می یابد؛ این حلقه هرگز پایان نمی یابد، زیرا ارزش عبارت 4>2
هرگز false نمی شود.
این ایده ی بدی است که عمداً یک حلقه ی بی نهایت ایجاد کنیم. اما، حتی برنامه نویسان با تجربه هم به طور تصادفی حلقه های بی نهایت را ایجاد کرده اند. بنابراین، قبل از اینکه شروع به نوشتن یک حلقه کنید، خوب است بدانید که چگونه از یک حلقه ی بی نهایت، در صورتی که در یکی از آنها قرار داشته باشید، خارج شوید.
اگر در یک حلقه، یک مقدار به طور مکرر در خروجی نمایش یابد، یا اگر صفحه ی نمایش برای یک مدت زمان طولانی، بدون نمایش دادن یک خروجی مشخص، فعالیتی انجام ندهد، می توان به آن، بعنوان یک حلقه ی بی نهایت مشکوک شد؛
اگر فکر می کنید در اپلیکیشن شما یک حلقه ی بی نهایت وجود دارد، می توانید کلید Ctrl را فشار دهید و نگه دارید، و کلید C یا Break را فشار دهید؛ با انجام این کار، حلقه ی برنامه باید خاتمه یابد. (در بسیاری از کیبوردها، کلید Break، کلید Pause نیز هست).
برای جلوگیری از اجرای یک حلقه به صورت بی نهایت، سه عمل جداگانه باید انجام شود:
1. ابتدا در یک متغیر کنترلِ نام گذاری شده، یک مقدار برای شروع قرار می گیرد.
2. متغیر کنترل حلقه در دستور while تست می شود.
3. اگر ارزش عبارت تست شده true باشد، بدنه ی دستور while مقدار متغیر کنترل حلقه را تغییر می دهد. در نهایت ارزش عبارت تست شده برابر با false می شود و حلقه پایان می یابد.
همه ی این شرایط در مثال درون تصویر 6.4 گرد هم آمده اند. اول یک متغیر کنترل حلقه به نام loopCount ایجاد شده و برابر با مقدار 1 قرار می گیرد. دوم، دستور while(loopCount < 3)
تست می شود. سوم، بدنه ی حلقه اجرا می شود، زیرا متغیر کنترل حلقه، یعنی loopCount کمتر از 3 است.
توجه کنید که بدنه ی حلقه که در تصویر 6.4 نشان داده شده است، شامل دو دستور است که یک با استفاده از یک جفت آکولاد، یک بلوک را تشکیل می دهند. دستور اول، پیغام "Hello" را نمایش می دهد و دستور دوم، مقدار 1 را به متغیر loopCount اضافه می کند. بنابراین دفعه ی بعد که متغیر loopCount ارزیابی می شود، مقدار آن 2 خواهد بود. پس مقدار این متغیر، کمتر است 3 است؛ بنابراین بدنه ی حلقه، دوباره اجرا می شود. مقدار Hello برای بار دوم نمایش می یابد و loopCount برابر با 3 می شود. در نهایت چونکه ارزش عبات loopCount < 3
به صورت false ارزیابی می شود، حلقه پایان می یابد. سپس اجرای برنامه با دستورات بعدی ادامه می یابد.
(تصویر 6.4 ،یک حلقه ی while که عبارت Hello را دو بار نمایش می دهد)
توضیح عبارات شماره گذاری شده در تصویر:
- متغیر کنترل حلقه، مقدار دهی اولیه می شود.
- متغیر کنترل حلقه تست می شود.
- مقدار متغیر کنترل حلقه تغییر داده می شود.
این مهم است که مقدار متغیر کنترل حلقه، در داخل بدنه ی حلقه، تغییر داده شود. تصویر 6.5 کد مشابهی را نشان می دهد، اما آکولادها حذف شده اند. در این مورد، بدنه ی حلقه ی while در سمی کالنی که در انتهای دستور حاوی Hello است، پایان می یابد. یعنی عبارتی که 1 را به loopCount اضافه می کند، بخشی از بلوک حلقه به حساب نمی آید. بنابراین مقدار loopCount هرگز تغییر نمی کند و یک حلقه ی بی نهایت ایجاد می شود.
(تصویر 6.5 : یک حلقه ی while که پیغام Hello را به صورت بی نهایت نمایش می دهد، چون متغیر loopCount در بدنه ی حلقه تغییر داده نشده است)
توضیح شماره های درون تصویر بالا:
- این کار را انجام ندهید، متغیر کنترل حلقه (در حلقه) تغییر داده نمی شود.
- این تو رفتگی تاثیری ندارد.
مانند تصمیم گیری با دستور if که در فصل 5 درباره ی آن نکاتی را آموختید، قرار دادن سمی کالن در انتهای دستور، وقتی که با دستور while کار می کنید، مهم است. اگر یک سمی کالن، به صورت اشتباه در انتهای دستور while (loopCount < 3);
قرار گیرد، حلقه بی نهایت خواهد شد؛ همان طور که در تصویر 6.6 نشان داده شده است.
این حلقه یک بدنه ی خالی دارد، یعنی یک بدنه که هیچ دستوری در داخل آن وجود ندارد. بنابراین عبارت بولین مورد نظر ارزیابی می شود و چون ارزش آن true است، نوبت به بدنه ی حلقه می رسد. چونکه بدنه ی حلقه خالی است، هیچ کاری انجام نمی شود و عبارت بولین مورد نظر دوباره ارزیابی می شود. هیچ چیزی تغییر نکرده است، بنابراین، ارزش این عبارت هنوز true است؛ و دوباره نوبت به بدنه ی حلقه می رسد که خالی است. و همین طور این حلقه ی بی نهایت ادامه می یابد.
(تصویر 6.6 : یک حلقه ی while که به صورت بی نهایت اجرا می شود و هیچ خروجی ندارد زیرا بدنه ی حلقه خالی است)
توضیح عبارت شماره 1 در تصویر بالا: این کار را انجام ندهید، این سمی کالن باعث می شود، حلقه یک بدنه ی خالی داشته باشد.
این خیلی متداول است که مقدار یک متغیر کنترل حلقه، با اضافه کردن 1 به آن، یا افزایش یک واحدی آن(ncrementing) تغییر کند. اما تمام حلقه ها، با اضافه کردن مقدار 1 در آنها، کنترل نمی شوند. حلقه ی نشان داده شده در تصویر 6.7 دو بار پیغام Hello را نمایش می دهد؛ درست مانند حلقه ی درون تصویر 6.4؛ اما حلقه ی آن با تفریق عدد 1 از متغیر کنترل حلقه یا کاهش یک واحدی آن(decrementing) کنترل می شود.
(تصویر 6.7 : یک حلقه ی while که پیغام Hello را دو بار نمایش می دهد و متغیر loopCount را در بدنه ی حلقه، کاهش می دهد)
در قطعه کد نشان داده شده در تصویر 6.7 ، متغیر loopCount با مقدار 3 شروع می شود. متغیر loopCount از 1 بزرگتر است، بنابراین بدنه ی حلقه پیغام Hello را نمایش می دهد و مقدار loopCount را به 2 کاهش می دهد. دوباره، عبارت بولینِ مورد نظر در حلقه ی while ،تست می شود. چونکه 2 از 1 بزرگتر است، دوباره پیغام Hello نمایش می یابد و loopCount برابر با 1 می شود. حالا دیگر loopCount بزرگتر از 1 نیست، بنابراین حلقه پایان می یابد.
برای دو بار اجرا کردن یک حلقه، راه های زیادی وجود دارد. بعنوان مثال، می توانیم مقدار یک متغیر کنترل حلقه را برابر با 10 قرار دهیم و حلقه را تا زمانی که این مقدار بزرگتر از 8 است ادامه دهیم؛ و این مقدار را هر بار که حلقه اجرا می شود، یک واحد کاهش دهیم.
به طور مشابه، ما می توانیم متغیر کنترل حلقه را برابر با 12 قرار دهیم و تا زمانی که این متغیر بزرگتر از 2 است، ادامه دهیم و هربار که حلقه اجرا می شود، 5 واحد از آن کم کنیم. به طور کلی، ما نباید از چنین روش های غیرعادی برای شمارش تکرارها استفاده کنیم؛ زیرا به سادگی باعث گیج شدن برنامه می شوند.
برای اجرای یک حلقه، به یک تعداد خاص، شفاف ترین و بهترین روش، این است که متغیر کنترل حلقه را با 0 یا 1 شروع کنیم و آن را هربار که حلقه تکرار می شود، 1 واحد افزایش دهیم و وقتی که متغیر کنترل حلقه به حد مناسب رسید، حلقه را متوقف کنیم.
نکته: وقتی که در ابتدا برنامه نویسی را آغاز می کنیم، منطقی به نظر می رسد که مقادیر شمارنده را با 1 آغاز کنیم، و این روش به خوبی کار می کند. اما بسیاری از برنامه نویسان با تجربه، مقدار شمارنده را از 0 شروع می کنند، زیرا وقتی دارند با آرایه ها کار می کنند، به این روش عادت کرده اند و به آن نیاز دارند. وقتی که در فصل "مقدمه ای در مورد آرایه ها"، روش کار با آرایه ها را به شما نشان دادیم، یاد خواهید گرفت که عناصر آرایه ها از اندیس 0 شروع می شود.
نوشتن یک حلقه while نامحدود
ما مجبور نیستیم با اضافه کردن یا تفریق کردن یک مقدار، از متغیر کنترل حلقه، آن را تغییر دهیم. اغلب مقدار یک متغیر کنترل حلقه توسط محاسبات تغییر داده نمی شود؛ اما به جای آن، توسط ورودی کاربر(user input) تغییر داده می شود.
به یک حلقه که توسط کاربر کنترل می شود، یک حلقه ی نامحدود(indefinite) گفته می شود؛ زیرا ما نمی دانیم این حلقه در نهایت چند بار اجرا می شود. بعنوان مثال، شاید بخواهیم تا زمانی که کاربر تمایل به ادامه ی یک درخواست دارد، وظیفه ای را ادامه دهیم. در این صورت، هنگامی که داریم برنامه را می نویسیم، نمی دانیم این حلقه در نهایت دو بار، یا 200 بار یا ... اجرا می شود.
نکته: یک حلقه ی محدود(definite) حلقه ای است که توسط شمارشگرها کنترل می شود. یک حلقه ی نامحدود(indefinite) حلقه ای است که توسط رویداد ها(event) کنترل می شود. یعنی رویدادی رخ می دهد که تعیین می کند آیا حلقه ادامه یابد یا خیر.
یک اپلیکیشن را در نظر بگیرید که در آن به یک کاربر، موجودی بانکی او نشان می دهیم و آنگاه از او سوال می کنیم که آیا می خواهد سود سالانه ی حساب خود را مشاهده کند یا خیر. هر زمان که کاربر ادامه دادن را انتخاب می کند، یک موجودی حساب افزایش یافته نمایش داده می شود، که سود جمع شده ی یک سالِ بیشتر حساب او را نشان می دهد. در نهایت، وقتی که کاربر، خروج(exit) را انتخاب می کند، برنامه پایان می یابد. این برنامه در تصویر 6.8 نمایش داده شده است.
(تصویر 6.8 : اپلیکیشن BankBalance)
توضیح عبارت های شماره گذاری شده در تصویر بالا:
شماره 1: در فصل 2، یاد گرفتیم که از nextLine()
پس از ورودی های عددی استفاده کنیم تا کلید Enter قبل از استفاده مجدد از nextLine()
مصرف شود. برای اطلاعات بیشتر در این زمینه، به اینجا مراجعه کنید.
شماره 2: فراخوانی charAt(0)
اولین کاراکتر را از response دریافت می کند.
شماره 3: این دور دستور باعث می شوند که balance به دو رقم اعشار گرد شود.
برنامه ی نشان داده شده در تصویر 6.8 متغیرهای مورد نیاز را تعریف می کند؛ و یک ثابت(constant) را نیز برای 3% سود تعریف می کند و آنگاه از کاربر می خواهد تا موجودی(balance) را به او نشان دهد یا نه.
حالا اپلیکیشن یک پاسخ را از کیبورد بعنوان یک رشته(String) دریافت می کند. سپس از اولین کاراکتر این رشته، استفاده می کند تا مشخص کند که آیا حلقه باید ادامه یابد یا خیر. تا زمانی که کاربر بخواهد ادامه دهد، اجرای اپلیکیشن ادامه می یابد تا افزایش موجودی بانکی او را نمایش دهد.
حلقه ی درون اپلیکیشن در تصویر 6.8 با خطی شروع می شود که حاوی عبارت زیر است:
while(responseChar == 'y')
اگر کاربر هر مقدار دیگری به جز 'y' را وارد کند، بدنه ی حلقه هرگز اجرا نمی شود؛ و به جای آن، برنامه پایان می یابد. اما اگر کاربر مقدار 'y' را وارد کند، تمام دستورات درون بدنه ی حلقه اجرا می شوند. و اپلیکیشن، موجودی حساب(balance) را با مقدار نرخ سود(interest rate)، افزایش می دهد.
آنگاه مقدار balance در 100 ضرب شده و نوع داده ی حاصل شده را به integer تبدیل(casting) می کنیم؛ (بعنوان مثال، اگر یک موجودی حساب برابر با 10.635 داشته باشیم، به 1063 تبدیل می شود) و نتیجه را بر 100 تقسیم می شود(بعنوان مثال 1063 به 10.63 تبدیل می شود). فایده ی این دو دستور این است که تعداد اعشار موجودی حساب(balance) تبدیل به دو رقم تبدیل می کند.
پس از انجام این محاسبات، اپلیکیشن موجودی حساب(balance) جدید را نمایش می دهد و سول می پرسد که آیا کاربر به یک موجودی حساب(balance) دیگر نیاز دارد یا نه. سپس متغیر year افزایش می یابد و بدنه ی حلقه، با استفاده از یک آکولاد بسته، پایان می یابد.
پس از اینکه بدنه ی حلقه اجرا شد، کنترل به بالای حلقه منتقل می شود؛ و عبارت بولین مورد نظر که در بین پرانتز حلقه ی while قرار دارد، دوباره تست می شود. حالا اگر کاربر کاراکتر 'y' را وارد کند، وارد حلقه می شویم و فرآیند درون حلقه دوباره آغاز می شود.
تصویر 6.9 خروجی اپلیکیشن BankBalance را پس از اینکه کاربر مقدار 575.00 دلار را بعنوان موجودی اولیه وارد کند، نشان می دهد؛ به این صورت که، قبل از اینکه پاسخ 'n' را وارد کند، برای برای افزایش سود پرداختی، پنج بار پاسخ 'y' را وارد کند:
(تصویر 6.9 : نمونه اجرای اپلیکیشن BankBalance)
برنامه نویسان معمولاً از حلقه های نامحدود(indefinite) به هنگام اعتبارسنجی داده های ورودی استفاده می کنند. اعتبارسنجی داده ها فرآیندی است برای اطمینان از اینکه یک مقدار در یک محدوده مشخص قرار گیرد.
بعنوان مثال، فرض کنید نیاز داریم یک کاربر، یک مقدار که بزرگتر از 3 نباشد را وارد کند. تصویر 6.10 یک اپلیکیشن را نشان می دهد که از حلقه ی ورود داده ها بیرون نمی آید، تا زمانی که کاربر یک مقدار درست را وارد کند. اگر کاربر مقدار 3 یا کمتر را در اولین درخواست وارد کند، حلقه ای که در تصویر 6.10 به رنگ آبی نشان داده شده است، هرگز اجرا نمی شود.
اما، اگر کاربر یک عدد بزرگتر از 3 را وارد کند، حقله ای که به رنگ آبی است، اجرا می شود؛ و به کاربر امکان می دهد یک مقدار صحیح را وارد کند. هنگامی که کاربر به وارد کردن داده ی اشتباه ادامه دهد، حلقه ادامه می یابد. تصویر 6.11 یک اجرای معمولی را نشان می دهد.
نکته: تصویر 6.10 یک متد عالی را برای اعتبارسنجی داده ی ورودی نشان می دهد. قبل از اینکه وارد حلقه شویم، اولین ورودی(input) را دریافت می کنیم. این مقدار اولیه ممکن است مقداری باشد که از هرگونه اجرای حلقه جلوگیری کند. اولین دستور ورودی، که قبل از حلقه قرار دارد، قرائت انسداد(priming read) یا ورودی انسداد(priming input) نامیده می شود. آخرین دستور در داخل حلقه، مقدار ورودی بعدی را، برای همان متغیری که قبل از شروع حلقه بررسی می شود، به دست می آورد.
(تصویر 6.10 ،اپلیکیشن EnterSmallValue)
(تصویر 6.11 : اجرای معمولی برنامه ی EnterSmallValue)
برنامه نویسان تازه کار، به اشتباه، برای چک کردن داده های نامعتبر، اغلب به جای یک حلقه، از یک تصمیم گیری استفاده می کنند. یعنی با استفاده از یک دستور if درخواست می کنند که آیا داده ی مورد نظر نامتعبر است یا نه؛ اگر این داده نامعتبر باشد، آنها دوباره از کاربر درخواست خود را تکرار می کنند. اما، آنها فراموش می کنند که کاربر ممکن است داده های نادرست را چندین بار وارد کنند. معمولاً استفاده از یک حلقه، بهترین ساختار برای اعتبار سنجی داده های ورودی است.
دو عبارت صحیح و یک عبارت اشتباه
موضوع: ایجاد حلقه های while
1. یک حلقه ی متناهی(finite)، به تعداد دفعات خاصی اجرا می شود؛ و یک حلقه ی نامحدود(indefinite) حلقه ای است که هرگز پایان نمی یابد.
2. یک حلقه ی while که به درستی نوشته شده است، حاوی یک متغیر کنترل حلقه، که مقدار دهی اولیه شده است، می باشد که در عبارت while تست می شود و آنگاه در بدنه ی حلقه تغییر داده می شود.
3. در یک حلقه ی نامحدود(indefinite)، ما نمی دانیم که حلقه چند بار اجرا می شود.
پاسخ:
گزینه ی شماره 1 اشتباه است. یک حلقه که به تعداد دفعات خاصی اجرا می شود، یک حلقه ی معیّن یا محدود(definite) است. و یک حلقه که هیچ وقت پایان نمی یابد، حلقه ی بی نهایت(infinite) نامیده می شود.