آموزش برنامه نویسی سوئیفت (Swift): تصمیم گیری و حلقه ها – بخش چهارم


در بخش قبلی این سری مطالب آموزش برنامه نویسی سوئیفت به مبحث «عملگر، optional و مقادیر تهی» پرداختیم. در این نوشته قصد داریم به یادگیری دانشی بپردازیم که به هوشمندتر شدن برنامههای ما کمک میکند.
تصمیمگیری
ما هر روزه با موقعیتهای تصمیمگیری مختلفی رو به رو میشویم. هنگامی که صبح از خواب برمیخیزید تصمیم میگیرید که یک قهوه بخورید؛ اما برای خوردن قهوه باید تصمیم بگیرید که یک فنجان قهوه درست میکنید یا نه. ابتدا باید بررسی کنید که آیا قهوه دارید یا نه، سپس باید ببینید که آیا شکر دارید یا نه و سپس باید وجود شیر را بررسی کنید. در نهایت وجود یک فنجان تمیز را بررسی میکنید.
اگر پاسخ هر کدام از سؤالهای فوق منفی باشد، یا باید هیچ کاری نکنیم و یا این که ابتدا موارد مورد نیاز بری تهیه قهوه را فراهم بکنیم. همین نکته در مورد برنامهنویسی نیز صحیح است و میتوانیم با استفاده از گزارههای if تصمیمهایی اتخاذ کنیم. شکل ظاهری گزاره if در زبان برنامه نویسی سوئیفت به صورت زیر است:
در قطعه کد فوق دو مقدار بولی در ابتدا تعریف شدهاند که یکی hasCoffee و دیگری madeCup است. ما میدانیم که در آشپزخانه قهوه داریم؛ اما هنوز قهوه درست نکردهایم و از این رو hasCoffee به صورت درست (true) و madeCup به صورت نادرست (flase) است.
سپس گزاره if واقعی را میبینیم. ما از ساختار if hasCoffee برای بررسی مقدار hasCoffee استفاده میکنیم، اگر وجود قهوه به صورت true ارزیابی شود در این صورت کد موجود درون آکولادها اجرا میشود. اگر hasCoffee به صورت false باشد، از همه کدی که درون آکولاد قرار دارد عبور میکنیم و مقدار madeCup به صورت false خواهد بود.
عرف کدنویسی apple چنین است که باید پیش از مقادیر بولی از یک فعل ربطی استفاده کنیم. بدین ترتیب در ادامه میتوانیم بدون نیاز به مراجعه دوباره به نوع آن نوعش را تشخیص دهیم.
hasCoffee از سنت کدنویسی اپل استفاده میکند؛ اما میتوانیم در موارد نیاز از ساختار isSunny یا didRain نیز استفاده کنیم. برای مثال یک آزمون تست میتواند از userIsAtLeastThirteen استفاده کند. این ساختار نه تنها به ما اعلام میکند که باید انتظار مقدار درست یا نادرست داشته باشیم؛ بلکه حتی کمک میکند بدانیم مقدار مورد نظر چه مقداری میتواند داشته باشد. برخی اوقات سرنخهای زمینهای مانند این به یادآوری متغیرهای دیگری که در زمانهای قبل در برنامه خود تعیین کردهایم کمک میکند.
در ادامه میخواهیم تواناییهای برنامهنویسی خود را افزایش دهیم و امکان تصمیمگیریهای بهتر را ایجاد کنم، زیرا گزارههای if بخش دیگری نیز دارند که امکان اجرای کار خاصی در صورت رد شدن شرط مورد بررسی را فراهم میآورند.
عبارت Else این امکان را به ما میدهد که در صورت نادرست بودن نتیجه ارزیابی شرط خود کد خاصی را اجرا کنیم. در این حالت hasCoffee به صورت false تعیین شده است و madeCup نیز کلاً مقداردهی نشده است؛ اما در نهایت یک مقدار بولی خواهد داشت و از این رو در ابتدا آن را به صورت یک مقدار بولی بدون مقدار تعریف میکنیم. دقت کنید که اگر تلاش کنید از یک مقدار بولی غیر اختیاری که وهلهسازی نشده است استفاده کنید، کامپایلر اعلام خطا خواهد کرد.
زمانی که وارد گزاره if میشویم، ابتدا دستور if hasCoffee را بررسی میکنیم و چون قهوه نداریم، به بخش else میرویم و madeCup را به صورت false تعیین میکنیم. برخی توسعهدهندگان گزارههای else/if دیگری را نیز برای بررسی حالتهای مختلف اضافه میکنند. بدین ترتیب کد به صورت زیر درمیآید:
در مثال فوق ابتدا age را به میزان 16 تعیین میکنیم و ageDescription به صورت نوع string اعلان میشود. گزاره if ابتدا بررسی میکند که آیا if age < 13 است یا نه. از آنجا که سن برابر با 16 است بنابراین گزاره شرطی فوق درست نیست و باید به گزاره else برویم. بنابراین کد else if age <= 18 را داریم که نتیجه این ارزیابی درست است و از این رو مقدار ageDescription به صورت ".You are a teenager" تعیین میشود. از آنجا که یکی از شاخههای گزاره شرطی ما درست بوده است، باید از بررسی حالتهای دیگر اجتناب کنیم و از گزاره if بازگردیم. به بیان دیگر هرگز به گزاره else مراجعه نمیکنیم.
روش دیگر نوشتن کد به صورت زیر است و این نیز ساختار معتبری است، صرفاً خواندن آن کمی دشوارتر از حالت قبلی است:
ساختار فوق به نام گزارههای if تودرتو شناخته میشود. در این کد میتوانید ببینید که نخستین بررسی یعنی age < 13 نتیجه false دارد و سپس age <= 18 بررسی میشود که آن نیز نتیجه false دارد و سپس به گزاره else منتقل میشویم که عبارت زیر را تعیین میکند:
در قطعه کد زیر تلاش کنید دریابید متغیر ageDescription چه مقداری میتواند داشته باشد. بدین ترتیب میتوانید میزان یادگیری خود از مقاله قبلی در مورد عملگرها و ترتیب آنها را بیازمایید.
در قطعه کد فوق ابتدا باید بررسی کنیم که آیا شخص برای قهوه خود به یک افزودنی نیاز دارد یا نه و اگر چنین است باید مطمئن شویم که آن افزودنی را داریم یا نه. بدین ترتیب از درست کردن قهوه اطمینان مییابیم. بدین ترتیب کد فوق از درست کردن قهوه بدون داشتن ماده اصلی جلوگیری میکند.
دقت کنید که در کد فوق پرانتزها تقدم را از همه عملگرها میگیرند، یعنی قبل از عملگرهای دیگر ارزیابی میشوند. بدین ترتیب عملگر پرانتز نسبت به عملگر (!) اولویت دارد و عملگر (!) نیز نسبت به عملگر (&&) اولویت دارد، در نهایت عملگر (&&) نسبت به عملگر (||) اولویت دارد. با استفاده از این منطق باید بتوانید حدس بزنید که آیا فنجان قهوه آماده خواهد شد یا نه.
گزارههای IF نیز دارای عملگر سهتایی هستند که در مقاله قبلی به اجمال اشاره کردیم؛ اما توضیح آن را به این مقاله موکول نمودیم.
عملگرهای سهتایی شبیه به گزارههای if استفاده میشوند و تا حدودی مانند عملگرهای nil-coalescing هستند. در ادامه مثالی از یک گزاره if نمایش مییابد و سپس روش کوتاه شده انجام همان گزاره if به صورت عملگر سهتایی را ملاحظه میکنید:
در مثال فوق، isSunShining مورد بررسی قرار میگیرد. عملگر (?) بدین معنی است که همه عبارتهای قبل از آن باید مورد بررسی قرار گیرند، عملگر (:) به این معنی است که عبارت سمت چپ باید در صورت درست بودن گزاره مورد بررسی و عبارت سمت راست در صورت نادرست بود گزاره مورد بررسی، استفاده شود. اگر بخواهیم به طور خلاصه آن را بخوانیم به این ترتیب است که مقدار description را به صورت زیر تعیین کن: ابتدا isSunShining را بررسی کن، اگر درست بود از «!Yay» و در غیر این صورت از «...Aww» استفاده کن.
همان طور که میبینید با بهرهگیری از این روش به جای 4 خط کد کافی است یک خط کد بنویسیم. عملگرهای سهتایی جالب هستند چون خوانایی بالایی دارند و این امر موجب بهبود زیادی در کد میشود؛ با این وجود مشکل آنها در زمان استفاده از گزارههای else if است که باید از گزارههای if استفاده کنیم و منتظر بهینهسازی کامپایلر بمانیم.
بهینهسازی کامپایلر در هنگام کامپایل شدن برنامه رخ میدهد و در این فرایند کامپایلر به دنبال چیزهایی میگردد که میتواند مورد استفاده مجدد قرار دهد و به این ترتیب مطمئن میشود که کد کوچکتر شده و کارایی بیشتری در استفاده از متغیرها و منطق برنامه پیدا میکند. به کد زیر توجه کنید:
همان طور که میبینید برخی حالتها هستند که استفاده از گزارههای if/else-if/else بسیار بهتر از عملگر سهتایی است؛ اما مطمئناً مواردی نیز وجود دارند که عملگرهای سهتایی میتوانند کارهای شما را به عنوان یک برنامهنویس، آسانتر سازند. ابزاری وجود دارد که آن را «جدول ارزش» (truth table) مینامیم. هدف آن نمایش نتایجی است که ممکن است در حالتهای مختلف به دست بیاوریم.
در جدول ارزش فوق، وضعیتهای ممکن مختلف برای عملگر AND و عملگر OR را مشاهده میکنید. AND برای بررسی True بودن همه متغیرها استفاده میشود. اگر یک متغیر دیگری داشتیم باید آن را در ستون دیگری از جدول به نام C قرار میدادیم و درستی آن را نیز بررسی میکردیم تا همه حالتهای ممکن بین سه متغیر A، B و C به دست آیند. اگر یکی از این متغیرها False باشند، در این صورت نتیجه False خواهد بود. حتی اگر 100 ستون داشته باشیم و تنها یک از متغیرها False باشد، در این صورت نتیجه False خواهد بود.
در سمت مقابل برای عملگر OR کافی است که یکی از مقادیر True باشد، یعنی اگر 100 ستون داشته باشیم و 99-تای آنها False و تنها یکی True باشد، در این صوت نتیجه True خواهد بود.
همان طور که میبینید این حالت شبیه به دموکراسی یا میانگین وزندار نیست. البته اگر چنین چیزی را بخواهیم میتوانیم منطق خاص خود را به وسیله شمارش تعداد مقادیر True، تعداد مقادیر False و مقایسه بزرگی و کوچکی هر کدام پیادهسازی کنیم. ما این موضوع را در ادامه بررسی خواهیم کرد؛ اما فعلاً به بررسی گزارههای if با عملگرهای منطقی و شیوه استفاده از آنها به صورت ترکیبی میپردازیم.
حلقهها
دو نوع متفاوت از حلقهها وجود دارد که در مورد آنها در ادامه این مقاله صحبت خواهیم کرد. نوع اول حلقههای while و نوع دوم حلقههای for-in هستند.
حلقههای while
حلقههای while به طور خلاصه حلقههایی هستند که «تا وقتی» (while) یک شرط برقرار باشد اجرا میشوند. حلقههای while دو شکل دارند که یکی while و دیگری repeat-while است. هر دوی آنها را میتوانید در کد زیر ملاحظه کنید:
تفاوت بین یک حلقه while و حلقه repeat-while در این است که حلقه while شرط حلقه را پیش از اجرای منطق حلقه بررسی میکند؛ اما حلقه repeat-while شرط حلقه را پس از اجرای منطق آن بررسی میکند. در کد زیر میتوانید شرایط استفاده از هر کدام را مشاهده کنید:
آیا متوجه میشوید که اشکال کار کجاست؟ ما نمیخواستیم قهوه درست بکنیم؛ اما در حلقه repeat-while در هر حالت یک فنجان قهوه درست میکنیم، زیرا شرط حلقه تا زمانی که منطق آن یک بار اجرا نشده باشد، مورد بررسی قرار نمیگیرد. با این حال حلقه while چنان که انتظار داریم اجرا میشود.
استفاده از حلقه repeat-while در کد فوق مانند آن است که کسی بگوید یک فنجان قهوه به من بده و نوع آن مهم نیست چون قصد نوشیدن آن را ندارم. اما حلقه repeat-while نیز موارد استفاده خاص خود را دارد. برای نمونه به کد زیر نگاه کنید:
در کد فوق برای ما مهم نیست که امتیازها از winningScore بالاتر برود یا نه. ما صرفاً میخواهیم بدانیم چه کسی امتیازاتش از winningScore بالاتر میرود. در این مثال شخص مورد نظر بازیکن شماره دو است که امتیازش دو برابر است؛ اما این منطق حلقه است که تعیین میکند بازی فعال باشد، در زمان فعال بودن چه اتفاقی بیفتد و چه زمانی بازی خاتمه بیابد.
در زمان نوشتن حلقهها باید مراقب باشیم. چون ممکن است به یک حلقه نامتناهی برسیم یعنی حلقهای که هرگز پایان نمییابد. بدین ترتیب ممکن است رایانه شما برای مدتی از کار بیفتد و برنامه همچنان مشغول محاسبات اجرای حلقه باشد. در کد زیر سناریوی دیگری را ملاحظه میکنید. آیا میتوانید اشکال آن را پیدا کنید؟
شاید بهتر باشد برای این که کد فوق را بهتر درک کنیم به جای نامهای متغیرها، مقادیرشان را قرار دهیم. در کد زیر این کار را انجام دادهایم:
بدین ترتیب 100- منهای 3 برابر با 103- خواهد بود و 103- همچنان کمتر از 25 است. در واقع تا هر زمان که مقادیر 3 را از این مجموع کم کنیم مقدار آن همیشه کمتر از 25 خواهد ماند. به مثال زیر نیز توجه کنید:
این حلقه هرگز متوقف نخواهد شد. روشی برای خروج از حلقه وجود دارد که در آن از گزارههای if استفاده کنیم. در کد زیر شیوه استفاده از break را مشاهده میکنید:
کلیدواژه break به برنامه اعلام میکند که از حلقه خارج شود. بنابراین در مثال فوق به محض این که شرط count == 100 برقرار شود، منطق درون گزاره if را اجرا میکنیم. به برنامه اعلام میکنیم که حلقه را متوقف (break) کند و آنچه را پس از آکولاد بسته در حلقه while آمده است اجرا میکنیم.
در مثال فوق اگر بخواهید شمارش را به صورت جفتی اجرا کنید، یک کلیدواژه وجود دارد که امکان رد شدن از برخی تکرارهای حلقه را در اختیار ما قرار میدهد. با بهرهگیری از مثال فوق، کد را طوری تغییر میدهیم که مضارب خاصی از اعداد را بشمارد.
اگر منطق موجود در کد فوق را متوجه میشوید، بدیهی است که اینک میتوانید اهمیت انتخاب نامهای گویا برای متغیرها، جهت افزایش خوانایی کد را درک کنید. در ادامه کد فوق را به صورت خط به خط توضیح میدهیم:
- while true – به معنی اجرا به مقدار نامتناهی است. true ثابتی با مقدار 1 است و false مقدار 0 دارد.
- if currentNumber% numberToCountBy!= 0 – اگر باقیمانده 11/4 به صورت 0 نباشد، 1 واحد به currentNumber اضافه میشود، از این رو در یک حلقه نامتناهی نمیافتیم و میتوانیم با استفاده از continue از این تکرار حلقهها رد شویم. اگر اجرای منطق حلقه در این مرحله پایان نیابد، currentNumber به آن اضافه میشود. میدانیم که وقتی این کد اجرا شود باید بر اساس منطق قبلی عددی باشد که بر numberToCountBy تقسیمپذیر باشد.
- currentNumber += 1 – شاید این قطعه کد شبیه قطعه قبلی باشد؛ اما در عمل چنین نیست. اگر currentNumber بر numberToCountBy بخشپذیر نباشد، در این صورت 1 را به currentNumber اضافه میکنیم و از همه موارد دیگر رد میشویم. اگر گزاره if به صورت false ارزیابی شود، هیچ کاری روی currentNumber صورت نخواهد گرفت.
در نهایت برای اطمینان از این که هرگز حتی در صورتی که currentNumber بر numberToCountBy تقسیمپذیر نباشد، در حلقه نامتناهی نخواهیم افتاد، دستور { if currentNumber == 100 { break را به کد خود اضافه کردهایم. همان طور که ملاحظه میکنید، این کد در یک خط نوشته شده است و این کار صرفاً با استفاده از space برای افزایش خوانایی در گزاره if با منطق زیاد انجام یافته است.
گزارههای چندخطی if را با استفاده از ; در انتهای هر گزاره نیز میتوانیم در یک خط بنویسم. برای نمونه:
البته دقت کنید که این وضعیت باعث میشود خوانایی کد کاهش یابد.
حلقههای for-in
حلقههای for-in مشابه حلقههای while هستند. حلقههای for-in به طور عمده در آرایهها و دیکشنریها استفاده میشوند؛ اما میتوان از آنها روی کاراکترها و اعداد نیز استفاده کرد. ظاهر حلقههای for-in به صورت زیر است:
در ادامه سعی میکنیم کد فوق را توضیح دهیم. نخستین چیز حلقه for-in است. حلقه for-in برای آغاز به چند چیز نیاز دارد. ابتدا باید یک متغیر داشته باشد که در بدنه حلقه استفاده خواهد شد. این متغیر را نمیتوان تغییر داد؛ اما میتوانید برای هر بار تکرار حلقه یک نام معنادار برای آن تعیین کنید. در مثال اول، ما از for name in names استفاده کردهایم. این کد به این صورت خوانده میشود: «برای name کنونی روی آرایهای از names تکرار میکنیم».
سپس منطق حلقه یعنی کارهایی که میخواهیم در آن اجرا شوند را اضافه میکنیم. در این مثال از یک رشته کاملاً طولانی استفاده شده است که همه نامها با استفاده از الحاق رشتهای به آن اضافه میشوند. زمانی که حلقه روی همه نامها اجرا شود، از آن خارج میشویم.
دیکشنریها کمی متفاوت هستند؛ اما ایده اصلی آن مشابه آرایه است. در مثال فوق با نوعی ساختار عجیب به صورت (for (name, type مواجه شدهایم. این ساختار در اصل، نوع دیگری را معرفی میکند که تاکنون در مورد آن صحبت نکردهایم و آن چندتایی (tuple) است.
یک چندتایی در واقع فهرستی از متغیرهای جدا شده با کاما است که درون پرانتز قرار دارند و شامل یک مقدار برای هر متغیر در فهرست هستند. این متغیرها به صورت متغیرهای منفرد قابل ارسال به نقاط مختلف هستند. ما این ساختار را در ادامه بیشتر توضیح خواهیم داد، اما فعلاً اطلاع در همین حد کفایت میکند.
دیکشنریها برای هر آیتمی که در خود جای میدهند، دو بخش دارند. این دو بخش به صورت کلید (key) و مقدار (value) هستند. «کلید»، بخش نخست دیکشنری است و «مقدار» بخش دوم آن است. کلیدها برای دسترسی به مقادیر استفاده میشوند. اگر تاکنون از یک دیکشنری واقعی استفاده کرده باشید، میدانید که برای یافتن هر واژه باید از یک کلید استفاده کنید و تعریف واژه را بر همین اساس پیدا کنید.
زمانی که از (for (name, type استفاده میکنیم، میتوانیم آن را به طور مستقیم به صورت for (key, value) in dictionary در نظر بگیریم. بنابراین با این معلومات به توضیح کد میپردازیم. ما در کد فوق روی هر یک از جفتهای کلید-مقدار موجود در دیکشنری حلقه را تکرار میکنیم و به هر کلید یا name که میرسیم آن را به petNames الحاق میکنیم. برای هر مقدار یا type بررسی میکنیم که آیا کلیدی برای آن type داریم یا نه. در حالت petTypeCounts کلیدها به صورت “Dog”, “Cat”, “Mouse” یا “Cricket” هستند. از آنجا که ممکن است کلیدهای دیکشنری موجود باشند یا نباشند باید از آنها به صورت optinal استفاده کنیم. بنابراین ابتدا بررسی زیر را اجرا میکنیم:
اگر چنین باشد باید یک مقدار اولیه برابر با 1 تعیین کنیم، زیرا تکرار کنونی حلقه این نوع را دارد. در غیر این صورت یک مقدار در [petTypeCounts[name داریم و کافی است مقدار مورد نظر را بهروزرسانی کنیم. از آنجا که این مقدار به صورت optional است؛ میتوانید آن را با استفاده از عملگر (!) به حالت force unwrap دربیاورید؛ اما در هنگام انجام این کار باید کاملاً مراقب باشید، زیرا همیشه میبایست مطمئن باشید که مقدار قبلاً تهی نباشد، در غیر این صورت عملگر (!) موجب از کار افتادن برنامه میشود. به طور خلاصه در صورتی که یک مقدار تهی را به اجبار unwrap بکنید، برنامه از کار میافتد.
در نهایت بازهها (ranges) را بررسی میکنیم. شما میتوانید یک بازه را به چند روش مختلف که در ادامه معرفی میکنیم ایجاد کنید:
- (...) یعنی «روش کاملاً باز» - برای مثال (10 ... 1). این روش مانند آن است که اعداد 1 تا 10 را پشت سر هم بنویسید.
- (>...) یعنی «روش نیمه باز کمتر از» - برای مثال (10< ... 1) مانند این است که اعداد 1 تا 9 را بنویسیم.
- (... 2) یعنی «روش کاملاً باز یکطرفه» همانند این است که اعداد 2، 3، 4، 5، 6، و ... را تا بینهایت بنویسیم.
- (2>...) «روش یکطرفه کاملاً باز» امکان بررسی هر مقداری کمتر از 2 را میدهد (تا بینهایت منفی).
در مثال آخر قطعه کد فوق، روش استفاده از حلقه for-in در یک بازه را نشان دادهایم. ابتدا یک متغیر به نام value برای استفاده در حلقه ارائه میکنیم. سپس مقدار count را به آن میدهیم تا بدانیم از چه عددی باید شروع کنیم و در نهایت یک بازه کاملاً باز از اعداد به صورت 1 ... 10 تعریف میکنیم. این حلقه روی همه اعداد اجرا میشود و آنها را یک به یک با هم جمع میزند.
اینک میتوانیم از شما بخواهیم برنامهای بنویسید که همه اعداد بخشپذیر بر 3 را که بین 57 و 398 قرار دارند، ارائه کند و شما نیز میتوانید این کار را با معلوماتی که از این راهنما کسب کردهاید انجام بدهید. اگر به قدر کافی در برنامهنویسی خبره شوید، این کار حتی به صورت ذهنی نیز ممکن خواهد بود. نکته جالب در مورد برنامهنویسی این است که زمان زیادی را صرف نوشتن کدی میکنید که رایانه محاسبات آن را در کمتر از ثانیهای انجام میدهد.
جمعبندی
اینک به پایان این راهنمای نسبتاً بلند رسیدیم؛ اما مسلماً ارزش مطالعه را داشت و با مفاهیم جالبی آشنا شدیم. در این مقاله شیوه اجرای تصمیمگیری در برنامهها را آموختیم و روش اجرای حلقه روی مجموعهای از دادهها و اعداد را فرا گرفتیم. بدین ترتیب مفاهیمی که در این نوشته معرفی شدند، کاربرد زیادی دارند و به شما در زمینه برنامهنویسی کمک زیادی میکنند.
برای مطالعه قسمت بعدی این مطلب روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی Swift (سوئیفت) برای برنامه نویسی iOS
- مجموعه آموزش های پروژه محور برنامه نویسی
- آموزش آرایه در برنامه نویسی Swift (سوئیفت)
- آموزش برنامه نویسی سوئیفت (Swift): متغیر، ثابت و انواع داده
- آموزش برنامه نویسی سوئیفت (Swift): اشاره گرها و انواع داده
- پوش نوتیفیکیشن (Push Notification) در iOS با استفاده از Swift — به زبان ساده
==