حلقه ها (ساختارهای تکرار) در کانستراکت
#1
Lightbulb 
به نام خدا
سلام دوستان امیدوارم حالتون خوب باشه...
امروز قصد دارم راجع به حلقه ها (Loops) یا ساختارهای تکرار در کانستراکت صحبت کنم. اگر تجربه برنامه نویسی با زبان های رایج برنامه نویسی رو داشته باشین بدون شک کلمه حلقه براتون آشناست. ساز و کار حلقه ها در کانستراکت بسیار مشابه زبان های رایج برنامه نویسی هست. در واقع یک حلقه مجموعه ای از دستورات از پیش تعیین شده است که میتونه در چند فاز (Phase) یا مرحله و به مدت چند مرتبه تکرار و اجرا بشه. فرض کنین یک برنامه کتابداری داریم که قصد داره بین کتابهایی که براش تعریف کردیم به دنبال یک کتاب خاص بگرده. اینجاست که به کمک حلقه ها میتونیم به راحتی و بدون سختی این مسئله رو پیاده سازی کنیم. پس با مطالبی که تا به اینجا ذکر شد به کمک حلقه ها:
  • پیاده سازی مسیرهای اجرایی و دستورات پیچیده بسیار ساده تر خواهد شد چرا که در یک حلقه مسئله به قسمت های کوچکتری تقسیم شده و مفسر کانستراکت به صورت قدم به قدم اقدام به حل مسئله می کند.
  • با استفاده از حلقه ها به جای روش های غیر اصولی و نادرست که ارمغانی جزء پرفرمنس (Performance) ضعیف، افزایش حجم برنامه و همچنین ناخوانایی ایونت ها برای ما نخواهند داشت می توانیم مشکلات ذکر شده را برطرف کنیم. 
  • قادر هستیم یک مجموعه از دستورات یا اکشن های مشخص را چندین مرتبه تکرار کنیم تا به نتیجه مطلوب برسیم بدون اینکه مجبور به کپی و پیست کردن اکشن ها و دستورات باشیم.
  • می توانیم دستورات یا اکشن ها را تحت شرایط و تدابیر خاصی اجرا کرده و تا زمانی که یک شرط مشخص برقرار نشده به اجرای دستورات یا اکشن ها ادامه دهیم.

تفاوت میان حلقه ها و توابع بازگشتی (Recrusion Functions) : 
 اگر با بحث توابع در کانستراکت آشنا باشین (در صورتی که آشنا نیستید حتماً قبل از ادامه مبحث توابع را از اینجا مطالعه کنید) به خوبی میدونید که در کانستراکت مشابه زبان های رایج برنامه نویسی دو دسته تابع یا فانکشن داریم که عبارتند از توابع بازگشتی و توابعی که خودشون رو صدا میزنن. روی صحبت من در این بخش توابع بازگشتی هستن. 
گاهی اوقات توابع میتونن خودشون رو صدا بزنن. تابعی که خودش رو صدا بزنه یا فراخوانی کنه تابع بازگشتی نامیده میشه. در واقع در شیوه بازگشتی مسئله به قسمت های کوچکتری تقسیم میشه و به صورت نامحدود یکسری روندها رخ میده. مثلاً محاسبه فاکتوریل یک مثال بارز عمل بازگشت میتونه باشه. در واقع در کانستراکت و بسیاری از زبان های رایج توابع بازگشتی با نام For و While شناخته میشن. هر تابع بازگشتی شامل سه فاز (Phase) یا مرحله به نام های ساده سازی و تکرار، شرط خاتمه و بازگشت میشه. گفتیم که یک تابع بازگشتی یک مسئله پیچیده رو به قسمت های کوچکتر تقسیم میکنه که این کار در فاز اول اجرای یک تابع بازگشتی انجام میشه. وقتی شرط خاتمه ای که برای ساده سازی و تکرار تابع بازگشتی تعریف کردیم برقرار بشه، تابع وارد فاز بازگشت میشه. در این فاز یا مرحله تابع از نتایجی که در فاز اول (ساده سازی و تکرار) به دست آورده استفاده میکنه تا بتونه یک خروجی به ما ارائه کنه. به طور ساده یک تابع بازگشتی به خودش متکیه و برای نمایش خروجی خودش از نتایجی که در هر مرحله به دست آورده استفاده میکنه. 
  • * تا به اینجای کار یک تابع بازگشتی شباهت بسیار زیادی به مفهوم حلقه (Loop) داشت. میتونیم تقریباً بگیم که حلقه در واقع همون تابع بازگشتی هست. اما تفاوت های ریزی میان حلقه و تابع بازگشتی وجود داره. در حین استفاده از حلقه یک یا چند خانه از حافظه رایانه اشغال میشه و اطلاعات اون خانه های اشغال شده از حلقه در هر فاز حلقه به طور مداوم بروزرسانی میشه. اما در استفاده از توابع بازگشتی استک ها (Stack) درگیر میشن که منابع حافظه کمتری نسبت به حلقه مصرف میکنن.برای اینکه شما رو از حالت سردرگمی در بیارم باید بگم که یک تابع بازگشتی تا حد زیادی معادل و هم ارز مفهوم حلقه است و یکسری تفاوت های ریزی مشاهده میشه که بهشون پرداختم. در بسیاری از زبان های برنامه نویسی رایج مفهوم تابع بازگشتی در قالب حلقه های For و While پیاده سازی شده که در کانستراکت هم همینطوره. 

انواع حلقه ها در کانستراکت: 
در دنیای کانستراکت 5 نوع حلقه یا ساختار تکرار داریم. این 5 نوع عبارت هستند از: 
  • * حلقه For: این حلقه قادره مجموعه ای از دستورات یا اکشن ها رو به تعداد دفعات مشخص که در شمارنده (Counter) اون تعریف میشه رو اجرا کنه. در واقع هر حلقه همونطور که در بخش قبل گفتیم شامل یک شرط خاتمه میشه. شرط خاتمه در این نوع حلقه رسیدن شمارنده به میزان مشخص شده در آرگومان ها (ورودی های) کاندیشن هست. کاندیشن For در کانستراکت شامل سه آرگومان Name، Start Index و End Index می باشد. آرگومان Name یک اسم برای حلقه For مشخص می کند که در حین بازرسی و عیب یابی کدها بتوانیم راحت تر بفهمیم که این حلقه چه وظیفه ای داشته. آرگومان Start Index فاز آغازین حلقه را مشخص می کند که می تواند از صفر مقداردهی شود. آرگومان End Index هم فاز پایانی یا نهایی حلقه را مشخص می کند. 
  • * حلقه For Each: این حلقه مجموعه ای از دستورات را به تعداد Instances (موارد) یک شیء در لایوت اجرا می کند. به طور مثال اگر از شیء Sprite سه مورد (Instance) در لایوت داشته باشیم دستورات موردنظر ما به میزان سه مرتبه اجرا می شوند و در هر فاز یا مرحله از این حلقه یک Instance از شیء Sprite مورد استفاده قرار می گیرد. شمارنده این حلقه همانطور که بیان شد بر مبنای Instance یک شیء در لایوت عمل می کند و شرط خاتمه آن رسیدن به آخرین Instance از شیء موردنظر می باشد.
  • * حلقه For Each (Ordered): این حلقه دقیقاً مشابه For Each عمل می کند با این تفاوت که در این نوع حلقه می توانیم در حین تنظیم حلقه یک اکسپرشن جهت فرمان دادن به حلقه به کار ببریم در حالی که در حلقه For Each چنین اجازه ای را نداریم. همچنین می توانیم راندمان اجرای مراحل حلقه را به صورت افزایشی (Ascending) و نزولی یا کاهشی (Descending) با توجه به اکسپرشن تعریف شده تنظیم کنیم. شرط خاتمه این حلقه رسیدن به آخرین Instance از شیء موردنظر می باشد.
  •  * حلقه Repeat: این حلقه مجموعه ای از اکشن ها را به تعداد دفعات مشخص اجرا می کند و مانند حلقه For شرط خاتمه آن رسیدن شمارنده به آخرین فاز از حلقه می باشد.
  • * حلقه While: این حلقه تعداد مراحل مشخصی ندارد و می تواند به سمت بی نهایت سوق پیدا کند. در واقع این حلقه به قدری اکشن های تعریف شده را تکرار می کند که حداقل یکی از کاندیشن های تعریف شده غلط از آب در بیاید. 
اکسپرشن کاربردی حلقه ها:  اکسپرشن loopindex اکسپرشنی است که مشخص می کند که در کدام فاز یا مرحله از هر نوع حلقه قرار داریم. در واقع این اکسپرشن مقدار آنی و لحظه ای شمارنده حلقه را بر می گرداند.
مفهوم حلقه بی نهایت یا حلقه بی فایده (Infinite Loop) : 
در صورتی که یک حلقه (به خصوص حلقه While) تعداد مراحل مشخصی نداشته باشد توسط مفسر کانستراکت یک حلقه بی نهایت یا بی فایده تلقی می شود و دستورات یا اکشن های حلقه به صورت متوالی و بدون توقف اجرا می شوند تا جایی که برنامه اصطلاحاً هنگ کند یا داون شود. در حین تعریف حلقه ها باید توجه زیادی به جلوگیری از ایجاد حلقه های بی نهایت داشته باشیم.
تبیین یک مثال جهت فهم بهتر حلقه ها (تبدیل اعداد باینری به دسیمال)  
برای ایجاد درک بهتر اجازه بدین یک مثال رو با هم بررسی کنیم. ما در اینجا قصد داریم اعداد صفر و یک (باینری) رو به دسیمال (ده دهی) تبدیل کنیم. به طور کلی فرآیند تبدیل اعداد باینری به دسیمال به این شکله که ما از سمت راست توان های عدد دو رو از صفر تا هر کجا که نیاز بود می نویسیم و بعد حاصل هر توان رو در مقدار باینری مربوطه خودش ضرب می کنیم و در نهایت حاصل ها رو با هم جمع می کنیم. فرض کنین قصد داریم مقدار باینری 101 رو به دسیمال (ده دهی) تبدیل کنیم. در این صورت داریم:
کد:
1   0    1
--------------
2^0 = 1 | 1 * 1 = 1
2^1 = 2 | 2 * 0 = 0
2^2 = 4 | 4 * 1 = 4
-----------------
4 + 0 + 1 = 5

(عبارت ^ به معنای توان و عبارت * به معنای ضرب است)
 خُب از سمت راست شروع می کنیم. اولین رقم باینری از سمت راست 1 هست. پس توان مربوط به این رقم میشه 0^2. حالا کافیه حاصل توان رو در مقدار باینری ضرب کنیم و در ذهنمون نگه داریم. برای بقیه ارقام باینری هم به همین ترتیب به پیش میریم و در نهایت سه عدد 4 و 0 و 1 رو با هم جمع میزنیم و عدد 5 رو به دست میاریم. پس یعنی مقدار باینری 101 در مبنای ده (دسیمال) میشه 5. حالا قصد داریم همین ساز و کار رو در کانستراکت برای هر عدد باینری دلخواهی پیاده سازی کنیم. بدون شک برای دریافت رقم به رقم عدد باینری و همچنین به توان رسوندن نیاز به حلقه داریم. البته من اینجا از تابع هم استفاده کردم.  
[تصویر:  bin2dec.png](در صورت عدم مشاهده تصویر اینجا کلیک کنید.)
خُب من در اولین کاندیشن یک تابع به نام bin2dec تعریف کردم و گفتم که در شروع اجرای تابع مقادیر ذخیره شده در شیء دیکشنری و مقدار متغیر value پاک بشه.
بعد یک حلقه For با مراحلی به تعداد کاراکترهای وارد شده در پارامتر تابع (تعداد ارقام عدد باینری) تشکیل دادم. در هر مرحله یا فاز از حلقه از سمت راست به کمک اکسپرشن tokenat ارقام خوانده میشن و هر یک از ارقام در کلیدهایی مثل value1، value2 و ... در شیء دیکشنری ذخیره میشن. در اصل برای این شروع فازهای حلقه رو صفر تعیین کردم که در به توان رسوندن اولین عدد از راست (چون باید بگم 0^2) مشکلی نداشته باشم. اما این تعیین عدد صفر به عنوان شروع کننده فازهای حلقه یه مشکل ایجاد میکنه و اونم اینکه که اکسپرشن tokenat شروع میکنه ارقام رو از چپ خوندن و ذخیره کردن چرا که این اکسپرشن مبنای کارش با Index صفر تعریف میشه. برای همین اکسپرشن tokenat رو به شکل زیر تنظیم کردم:
کد:
tokenat(trim(Function.Param(0)),len(trim(Function.Param(0)))-1-loopindex,"")
(اکسپرشن trim برای حذف فضاهای خالی به کار میره چرا که هر فاصله یا فضای خالی یک کاراکتر به حساب میاد و اکسپرشن len هم برای شمارش تعداد کاراکترهای پارامتر ورودی تابع بعد از حذف فضاهای خالی است.)
در واقع شاخص (Index) اکسپرشن tokenat رو در هر فاز از حلقه حاصل تفریق تعداد ارقام باینری و مرحله از حلقه و عدد یک قرار دادم. فرض کنین یک عدد باینری سه رقمی داریم و الان حلقه ما در فاز صفر به سر میبره. حالا این اکسپرشن قصد داره اولین رقم از سمت راست رو بخونه و به عنوان کلید value0 در دیکشنری ذخیره کنه. حاصل تفریق مرحله حلقه، تعداد ارقام باینری و عدد یک با این توضیحات میشه 0-1-3 = 2. یعنی اکسپرشن tokenat میاد Index دوم (یعنی آخرین رقم عدد باینری و به عبارتی اولین رقم از سمت راست عدد باینری) رو دریافت میکنه و در دیکشنری ذخیره میکنه.
  چون یک کلید اضافه و خالی تشکیل میشه اون یک کلید اضافه رو در آخرین فاز از حلقه حذف کردم. سپس گفتم که مقدار هر کلید تغییر کنه به مقدار به توان رسیده شده و بعد در نهایت مقادیر در یک متغیر به نام value با هم جمع بشن و خروجی تابع این متغیر باشه. در ایونت بعدی تعیین کردم که اگر نتیجه اکسپرشن uppercase برای پارامتر تابع برابر با صفر شد در اون صورت مشخص میشه که در میان ارقام وارده یک سری حروف الفبا انگلیسی هم موجوده و پیغام تعیین شده به عنوان خروجی تابع نمایش داده میشه. در واقع اکسپرشن uppercase وقتی حروف و اعداد با هم تلفیق بشن مقدار صفر رو بر میگردونه. در ایونت آخر (چهارم) هم مشخص کردم که اگر مقدار پارامتر ورودی به تابع تُهی بود پیغامی که تعیین کردم به عنوان خروجی تابع نمایش داده بشه.
امیدوارم این مطلب براتون مفید و کاربردی واقع شده باشه.
خوشحال میشم اگر جایی اشتباه یا نامناسب بیان شده به بنده در ادامه گوشزد کنین.
با آرزوی بهترین ها...
غایب
  پاسخ


 سپاس شده توسط: mhp ، rezamms ، M.gh ، MohammadHadi ، mostafanastary ، Alireza3d ، Hamed85 ، amin hosseini ، محمد40
#2
سلام Master Badfar 

والا این موضوعی که شما نوشتید شدیدا پیچیده هست و قطعا برای کاربران معمولی باعث سردرگمی میشه

پیشنهاد میکنم که آموزش هاتون رو به صورت ویدئویی ضبط و در انجمن منتشر کنید

اما در کل
پست بسیار مفیدی بود
  پاسخ


 سپاس شده توسط: M.gh ، Hamed85
#3
(1399/2/6، 03:19 عصر)rezamms نوشته است: سلام Master Badfar 

والا این موضوعی که شما نوشتید شدیدا پیچیده هست و قطعا برای کاربران معمولی باعث سردرگمی میشه

پیشنهاد میکنم که آموزش هاتون رو به صورت ویدئویی ضبط و در انجمن منتشر کنید

اما در کل
پست بسیار مفیدی بود
سلام رضای عزیز؛
کاملاً قبول دارم که برای کاربران تازه کار دشوار به نظر میرسه. از این به بعد هم سعی میکنم رویکرد کار رو ویدیویی کنم. 
ممنون بابت پیشنهادت
غایب
  پاسخ


 سپاس شده توسط: M.gh ، kakmamad
#4
عالی بود من بالاخره فهمیدم چیه این حلقه ها و....
ولی به قول آقا رضا بعضی جاهاش برای من که تازه کارم نامفهوم بود
ممنون.
  پاسخ


 سپاس شده توسط: Master Badfar ، kakmamad
#5

 خیلی عالی بود ممنون . اتفاقا دنبال همچین آموزشی بودم
از نظر بهینه سازی وافزایش پرفورمنس از کدوم یک از این ساختارها باید کمتر و از کدوم ببشتر استفاده کنیم . ممنون
  پاسخ


 سپاس شده توسط:
#6
(1399/2/9، 11:56 صبح)alirezaa82 نوشته است:  خیلی عالی بود ممنون . اتفاقا دنبال همچین آموزشی بودم
از نظر بهینه سازی وافزایش پرفورمنس از کدوم یک از این ساختارها باید کمتر و از کدوم ببشتر استفاده کنیم . ممنون
سلام به شما دوست عزیز؛
اینکه از کدوم یک از 5 حلقه استفاده کنین به شرایط و البته انتخاب خودتون بر میگرده. شما برای پرفرمنس باید حواستون باشه همونطور که گفتم از ایجاد حلقه های بی نهایت یا بی فایده جلوگیری کنین و همچنین اگر یه جایی دیگه لازم نبود حلقه پردازش انجام بده اون رو اصطلاحاً break کنین که به این منظور باید از اکشن Stop Loop استفاده کنین.
به طور کلی حلقه For کاربرد بیشتری داره و حلقه For Each Ordered کاربرد کمتری داره.
غایب
  پاسخ


 سپاس شده توسط: Alireza3d


موضوع‌های مشابه…
موضوع نویسنده پاسخ بازدید آخرین ارسال
Star مهم آموزش خروجی اندروید روی سیستم شخصی rezamms 127 76,096 1402/8/24، 09:00 عصر
آخرین ارسال: mehdiosw
  مهم آموزش تصویری خروجی مستقیم - یکبار برای همیشه! rezamms 33 17,646 1401/2/13، 09:39 عصر
آخرین ارسال: kamran_cn
  خروجی اندرید davinmstr1 2 2,142 1400/8/4، 10:23 عصر
آخرین ارسال: ᔕinaᗪehghani
  AAB (بسته برنامه اندروید) چيست؟ + نحوه خروجي گرفتن در كرودوا ᔕinaᗪehghani 15 6,991 1400/6/21، 01:55 صبح
آخرین ارسال: mehdi1100
  رفع مشکل خروجی فونگپ (: M.gh 11 7,298 1400/4/10، 02:17 صبح
آخرین ارسال: oak

پرش به انجمن: