توابع Iterator در لوا :
iterator به معنای لغوی تکرار کننده هست. در واقع در لوا ، ایتریتور به تابعی میگویند که با استفاده از هر نوع حلقه ای (مثل for و while و repeat که در بین این ها for رایج هست) ، آن تابع تکرار میشود تا زمانی که اولین مقدار بازگشتی آن تابع nil شود
ساختار کلی iterator ها به شکل زیر است :
برای مشاهده این لینک/عکس می بایست عضو شوید ! برای عضویت اینجا کلیک کنید
ایتریتورها میتوانند بصورت مستقیم از حلقه ها فراخوانی شوند (حلقه ی for) یا بصورت غیر مستقیم که در این صورت از کلمه ی کلیدی return استفاده میشود .
قبل از همه اول به مقدمه میپردازیم
همانطور که در موضوعات قبل بیان شد ، iterator ، نام یک تابع است و تابع iterator بالا که نامش FuncName هست ، 2 مقدار ورودی (آرگومان first و secand) و 2 مقدار خروجی (مقادیر out_1 و out_2) دارد که برای فراخوانی و استفاده از خروجی و مقادیر آن در حالت معمول و عادی به این روش است (مثلا فرض میکنیم نوع آرگومان ها ، رشته اند) :
که در اینجا مقدار out_1 داخل متغییر inp_1 و مقدار out_2 داخل متغییر inp_2 ذخیره میشودکد:inp_1,inp_2=FuncName("Word","Hello")
کلمه ی کلیدی in : کلمه ی کلیدی ایست که در حلقه ی for بکار میرود و برای تکرار یک تابع (iterator) تا اولین مقدار (بازگشتی) nil شدن آن بکار میرود . بطوری که در سمت چپ آن ، نام متغییری را مینویسیم که مقادیر آن تابع مورد نظر بازگشت میدهند و در سمت راست آن ، نام تابع iterator و همچنین مقادیر آرگومان آنرا برای فراخوانی تابع مینویسیم
نکته ها :
1) فراخوانی تابع معمولی که با علامت پرانتز انجام میشود اما تابع ایتریتور بجایش از علامت ویرگول (کاما) استفاده میشود یعنی آرگومان ها با نام تابع بجای علامت پرانتز ، با ویرگول از هم جدا میشوند . مشخص است که اگر تابع ایتریتوری آرگومان نداشته باشد ، فقط نام آنرا فراخوانی میکنیم بدون هیچ علامت ویرگول یا پرانتز ای
ایتریتورها فقط 2 جا فراخوانی میشوند . یا در حلقه ی for (منظور کنار کلمه ی کلیدی in در خطی که حلقه تعریف میشود نه داخل حلقه) یا به عنوان مقدار بازگشتی در تابعی دیگر (که در این صورت ، غیر مستقیم میشود)
بدیهی است که در هر کجای دیگر غیر از این حالت ، تابع ایتریتور فراخوانی شود (مثلا داخل حلقه ی for یا حتی خارج از آن) ، مثل حالت عادی ، برای فراخوانی تابع ایتریتور ، از علامت تابع که پرانتز بود استفاده میکنیم که در این صورت از ساختار تابع ایتریتور بالا استفاده نمیشود و مثل توابع معمولی بکار میاید (مثلا حتی اگر در حلقه for هم تابع ایتریتور را فراخوانی کنیم ، همیشه برای هر بار تکرار ، هر دو آرگومان تابع ایتریتور برابر مقدار اولیه ی خود میشوند)
2) نکته ی دیگر اینکه تعداد آرگومان های تابع ایتریتور نمیتواند بیش از 2 تا باشد (بصورت استاندارد مشکلی ندارند اما اگر آرگومان سوم به بعد در تابع ایتریتور فراخوانی شوند ، به عنوان nil شناخته میشود حتی اگر مقدار داشته باشد اما خروجی تابع میتواند هر چند تا باشد)
3) نکته ی بسیار حائز اهمیت این است که عملکرد iterator ها به این گونه است که ابتدا مقادیری که در حلقه ی for برای آرگومان های تابع iterator در نظر گرفتیم (در مثال بالا همان رشته های "Word" و "Hello") مثل حالت عادی ، به ترتیب در آرگومان های تابع شان جایگذاری و مقدار دهی میشوند (یعنی "first="Word و "secand="Hello میشود) . در تکرار دفعات بعد ، همیشه آرگومان اول ، مقدارش همان مقدار اولیه و ثابت است یعنی در مثال بالا ، همیشه "first="Word هست حالا هر چقدر که میخواهد تابع تکرار شود . خوب باز هم معلوم است که مقدار out_1 داخل متغییر inp_1 و مقدار out_2 داخل متغییر inp_2 ذخیره میشود (یعنی در مثال بالا inp_1=5 و inp_2=10 میشود) . نکته ی مهم اینجاست که در دفعات دوم و به بعد ، همیشه مقدار اولین متغییر (inp_1 که 10 هست) ، بجای دومین آرگومان (که اولین بار رشته ی "Hello" بود) ارسال و جایگزین و مقدار دهی میشود (یعنی در دومین تکرار ، بجای عدد 10 بجای رشته ی "Hello" به آرگومان دوم که secand هست مقداردهی میشود و آرگومان اول یعنی first هم که همیشه مقدار اولیه ی خود که رشته ی "Word" بود را حفظ میکند)
4) ما هیچ کنترلی بر متغییرها و مقادیرشان در حلقه ی for نداریم یعنی نمیشود مثلا مقدار متغییر inp_1 را که همان مقدار out_1 هست را (به منظور کنترل الگوریتم) از حلقه ی for تغییر داد . باید این کنترل ها را از خود تابع ایتریتور انجام داد . مثال این نکات در ادامه گفته خواهد شد
5) حلقه ی for تا آنجایی تکرار میشود که اولین مقدار بازگشتی تابع ایتریتور (یعنی مقدار out_1) برابر nil شود
6) در تابع ایتریتور ، اگر شرطی گذاشته شود (بدون اینکه مقدار nil را برگردانیم در هر کدام از حالات شرط) و اگر شرط نقض شود ، حلقه ی for از تکرار باز میایستد
7) در تابع ایتریتور (مثل متغییر محلی در حلقه ی for و ...) ، متغییر محلی کارایی ندارد و مثل متغییر عادی تا پایان آخرین تکرار این تابع ، مقدارش در هر بار تکرار محفوظ میماند.
8) تابع ایتریتور ، حتما باید مقداری را برگرداند (حداقل یک مقدار یا تابع یا هر چیز دیگری)
9) در لغات انگلیسی ، به اولین مقداری که در حلقه ی for به عنوان اولین آرگومان مقداردهی میشود (رشته ی "Word") میگویند invariant state (حالت ثابت) و به دومین مقدار ("Hello") میگویند متغییر کنترلی یا control variable
مثال ها :
1)
ابتدا در خط 8 ، تابع ایتریتور square با مقادیر iteratorMaxCount=3 (که تا آخر ، موقع فراخوانی مجدد تابع ایتریتور ، هر بار این مقدار ثابت میماند ولو حتی متغییر iteratorMaxCount در تابع ایتریتور یا هر کجای دیگر تغییر کند) و currentNumber=0 مقدار دهی میشود و تابع ایتریتور اجرا میشود . چون داخل تابع ایتریتور شرط بکار رفته ، پس طبق نکاتی که گفته شد ، اگر شرط نقض شود ، دیگر حلقه ی for اجرا نمیشود. شرط تابع که کاملا واضح و نیاز به توضیح ندارد .کد:function square(iteratorMaxCount,currentNumber) if currentNumber<iteratorMaxCount then currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end for i,n in square,3,0 do Dialog.Message("Notice", "i : "..i.."\nn : "..n, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
تابع ، مقدار 1 را به عنوان اولین مقدار که در متغییر i ذخیره میشود و مقدار 1 را باز هم به عنوان دومین مقدار که در متغییر n ذخیره میشود را بازمیگرداند . بعد هم که تمام دستورات حلقه ی for اجرا میشود پس در اینجا این دو مقدار را چاپ میکند .
برای اجرای بار دوم ، اولین آرگومان که iteratorMaxCount=3 بود ، تا ابد ثابت میماند. دومین پارامتر که currentNumber بود ، مقدار اولین متغییری که آخرین بار هر چه بود (متغییر i که مقدارش 1 بود) جایگزین و ارسال میشود یعنی این بار currentNumber=1 میشود . باز هم شرط برقرار است و اولین مقدار بازگشتی این بار 2 میشود که در i ذخیره و دومین مقدار هم که 4 میشود (2*2) که در n ذخیره میشود .
بار سوم باز هم iteratorMaxCount=3 و مقدار i که این بار 2 است به عنوان آرگومان دوم که currentNumber است ، مقداردهی میشود . در تابع ایتریتور ، اولین مقدار بازگشتی 3 و دومین مقدار بازگشتی 9 (3*3) میشود و در خروجی چاپ میشود . بار بعدی iteratorMaxCount=3 و currentNumber=3 میشود که شرط نقض شده و دیگر حلقه ی for از اجرا بازمیایستد
توجه : دقت کنیم که در مثال بالا ، مقدار آرگومان دوم تابع مان که currentNumber بود ، طبق آخرین مقدار currentNumber (که تصور کنید چون مقدار بازگشتی تابع مان currentNumber بود پس مقدار آرگومان دوم برای دفعه ی بعد ، از آخرین مقدار currentNumber) تغذیه میشود؛ این طور نیست. مقدار آرگومان دوم برای دفعه ی بعد ، همان مقدار اولین خروجی خود همان تابع هست (چه میخواهد اسمش همین currentNumber باشد مثل مثال بالا یا هر متغییر دیگری) . یعنی اگر یک متغییری مثلا بنام Value هم که بود و در اولین خروجی تابع قرار میگرفت و حتی اگر متغییر currentNumber هم در دومین خروجی تابع قرار میگرفت ، مقدار متغییر i (که همان برابر مقدار آرگومان یا ورودی دوم این تابع در دفعه ی بعد بود) همان برابر با اولین مقدار خروجی تابع که Value بود میشد
2) این مثال غلط است چون آرگومان تابع ، بیش از 2 تاست :
3)کد:function Init(a,b,c) Dialog.Message("2", "a : "..a.."\nb : "..b, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); b=b+1 a=a-1 function Sec(a,b) ABC = a+b+5 return ABC,a,b end return Sec(a,b) end for Fh,fd,re in Init,10,20,30 do Dialog.Message("15", "abc : "..Fh.."\na : "..fd.."\nb : "..re, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
در خط 12 که فراخوانی شد تابع ایتریتور در حلقه ، مقدار دهی انجام شد (first=0 که همیشه ثابت است در تکرار و اجرای دفعات بعد این تابع و secand=10) . همانطور که گفته شد ، در تابع ایتریتور (و هم هرحلقه ی تکراری) اگر متغییر محلی تعریف شود ، تا آخر اجرایش ، مثل متغییر سراسری ، مقدار قبل اش در آن حفظ میشود اما از بلاک خود خارج نمیشوند یعنی متغییر محلی ای که در تابع تعریف شد (اگر قبلا با همین نام ، متغییری نساخته باشیم) ، مقدارش در بلاک دیگر (مثل بالا که حلقه ی for است) ، nil میشود و برعکس . پس در مثال بالا نمیشود متغییر minuse را در حلقه ی for و متغییر multiple را در تابع ایتریتور فراخوانی کرد چون مقدارشان nil میشودکد:function iter(first,secand) first=first+1 local minuse=secand-1 if secand~=1 and new==nil then return minuse,first else new=nil return nil end end for input,useless in iter,0,10 do local multiple=input multiple=multiple^2 Dialog.Message("Notice", "input : "..input.."\nuseless : "..useless.."\nmultiple : "..multiple, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); if input==5 then new="down" end end
خوب در تابع ایتریتور ، شرط پابرجاست پس مقدار minuse که 9 میشود در متغییر input ذخیره میشود . حالا کدهای حلقه ی for اجرا میشود یعنی متغییرها چاپ میشوند و هم شرط (input==5) برقرار نیست تا متغییر new مقداردهی شود .
دراجرای بار دوم ، اولین آرگومان که ثابت هست یعنی first=0 هست (هر چند متغییر first در اجرای تابع ایتریتور در دفعه ی قبل ، یکی اضافه شده اما همونطور که گفته شد ، آرگومان های ایتریتور به این نوع مقادیر توجهی ندارد و مقدار آرگومان اول همیشه ثابت و برابر مقدار اولیه اش هست و آرگومان دوم هم برابر مقدار اولین متغییر (خروجی) که باز هم ربطی ندارد که هم نام متغییر قبل با هر الگوریمی در تهیه و مقداردهی آن متغییر باشد یا نه) و آرگومان دوم که همان اولین متغییر (خروجی) که در اینجا input و مقدارش 9 بود ، هست . بنابراین مثل دفعه ی قبل دستورات اجرا میشوند تا آرگومان secand یا همان متغییر input به 5 برسد . بعد حلقه ی for اجرا شده و چون مقدار input برابر 5 است ، متغییر new="down" میشود و در دفعه ی بعد اجرای تابع ایتریتور ، چون new مقدار دارد ، اولین خروجی تابع ، nil میشود که خود باعث توقف اجرای این حلقه میشود
توجه داشته باشیم که مثلا در حلقه ی for ، چون مقدار متغییر input همان مقدار آرگومان secand هست ، نمیتوانیم در این حلقه ، مستقیما مقدار input را عوض کنیم . یعنی مثلا input اگر به 5 رسیده باشد ، بگوییم input=1 کن .یعنی کنترل روی هر متغییر مخصوص به بلاک خودش ، جداست و یک روش آن ، کنترل توسط تعریف متغییر جدید میباشد
فراخوانی تابع iterator از تابعی دیگر (فراخوانی غیر مستقیم از حلقه ی for) :
حالت دیگر استفاده از تابع ایتریتور وجود دارد . در این حالت ، تابع ایتریتور از تابع دیگر (بصورت غیر مستقیم از حلقه ی for) فراخوانی میشود . در این حالت از کلمه ی کلیدی return در تابعی که تابع ایتریتور را فراخوانی میکند استفاده میشود
در این حالت ، 2 تابع میتوانند داخل هم قرار گیرند و یا جدا از هم باشند و نقطه اشتراک شان این است که بالاخره هر یکی باید دیگری (خارجی، داخلی) را فراخوانی کند
در این حالت ، بسته به استفاده و نوع فراخوانی مان از تابع ایتریتور (که علامتش ویرگول بود) ، هم میشود هر دو تابع را به عنوان تابع ایتریتور فراخوانی کرد و یا اینکه یکی از آنها را به عنوان تابع ایتریتور فراخوانی کنیم که در این صورت ، تابعی که بصورت عادی فراخوانی شده است (با علامت پرانتز)، فقط یکبار و بلافاصله بعد از آن ، تابعی که بصورت ایتریتور فراخوانی شده است (با علامت ویرگول) ، تا زمانی که nil را برگرداند ، تکرار میشود
اگر 2 تابع داخل هم قرار دهیم طوری که تابع داخلی بی نام باشد و به عنوان مقدار بازگشتی تابع خارجی باشد (کنار return) و تابع خارجی آنرا هم در حلقه ، بصورت عادی (نه بصورت ایتریتور) فراخوانی کنیم ، این تابع داخلی به عنوان تابع ایتریتور در نظر گرفته میشود
دقت داشته باشیم که در تابع ایتریتور ، هیچ گاه بیش از 2 تابع تو در تو (که تابع خارجی ، تابع داخلی را برگرداند) ، نمیشود استفاده کرد
مثال :
1)
همانطور که در موضوعات قبل گفته شد ، در خط سوم ، علامت # تعداد آرایه ی t را در n ذخیره میکند که 3 استکد:function list_iter (t) local i = 0 local n = #t return function () i = i + 1 if i <= n then return t[i] end end end t = {10, 20, 30} for element in list_iter(t) do Dialog.Message("Notice", element, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
در مثال بالا ، همونطور که از فراخوانی خط 13 (کنار حلقه for) پیداست ، چون تابع list_iter را با علامت پرانتز فراخوانی کردیم ، پس به عنوان تابع معمولی فراخوانی کردیم نه به عنوان تابع ایتریتور ، پس این تابع ، فقط یکبار اجرا میشود و از آنجایی که داخل این تابع ، تابع بی نام ای برگردانده شده است (خط 4) ، پس طبق مطلب بالا ، این تابع ، تابع ایتریتور ماست یعنی با اجرای این رویداد ، از 1 تا 3 ، فقط یکبار اجرا و تابع داخلی آن که از خط 4 تا 9 است ، به اندازه ای اجرا میشود تا مقدار nil را برگرداند و چون این تابع (خط 4 تا 9) تابع ایتریتور ماست ، پس باید مقداری را برگرداند
خوب چون تابع list_iter ما که در خط 13 فراخوانی شد ، تابع معمولی است ، پس معلوم است که هر چقدر میخواهد ، میتواند تعداد آرگومان داشته باشد و قواعد تابع ایتریتورها که در شکل اول آمده ، بر روی آن اعمال نمیشود اما از آنجا که خود تابع بی نام ای را برمیگرداند ، نمیتواند بیش از همین تابع ، مقدار و خروجی دیگری را بازگرداند
پس بعد از فراخوانی تابع list_iter و آرگومان t که آرایه هست به عنوان آرایه ، در خط دوم ، i=0 و n=3 میشود . (تا اینجا فقط همین یکبار اجرا میشوند و تمام) . وقتی در خط 4 به تابع ایتریتور میرسد ، در خط 5 ، مقدار i=1 میشود و چون در خط 6 شرط برقرار است (1<3) پس تابع ایتریتورمان [1]t را برمیگرداند که مقدارش 10 است . همانطور که میدانید ، این مقدار 10 که (اولین) مقدار بازگشتی تابع ایتریتورمان است ، در متغییر element در خط 13 ذخیره و در خط 14 چاپ میشود .
در اجرای بار بعد (چون تابع list_iter را در خط 13 بصورت عادی فراخوانی کردیم ، بدیهی است قضیه ی آرگومان های تابع ایتریتور اعمال نمیشود) دوباره تابع ایتریتورمان (خط 4 تا 9) تکرار میشود (تا شرط آن نقض شود) . پس در خط 5 مقدار i=2 میشود (قبلا گفته شد که متغییر محلی ، در هر حلقه از جمله تابع ایتریتور ، در هر بار اجرا ، مقدار قبل خود را حفظ میکند که البته فقط در بلاک خودش در دفعات بعد ، حفظ میشود مقدارش و اعتبار دارد نه در بلاک های دیگر مثل حلقه ی for) و [2]t که 20 است در متغییر element در خط 13 ذخیره و در خط 14 چاپ میشود .
در اجرای بار بعد هم همینطور و در نهایت مقدار 30 چاپ میشود
2)
حالا اگر همان مثال ، کل تابع list_iter رو تابع ایتریتور در نظر بگیریم (در فراخوانی حلقه ی for در خط 13 ، از ویرگول استفاده کنیم) چه اتفاقی رخ میدهد؟ (با کمی تغییرات)کد:function list_iter (t) local n = #t if i < n then return function () i = i + 1 return t[i] end end end i=0 t = {10, 20, 30} for element in list_iter,t do Dialog.Message("Notice", type(element).."\n"..element(), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
ابتدا در خط 11 مقدار i=0 و آرایه ی t تعریف شد
در خط 13 در حلقه ی for ، برای آرگومان ها از ویرگول استفاده شد پس یعنی کل تابع list_iter مان به عنوان تابع ایتریتور است و از آنجایی که همیشه اولین آرگومان تابع ایتریتور ثابت است و الان هم یک آرگومان بیشتر نداریم پس همیشه t مان ثابت و برابر آرایه ای که تعریف شد است
در خط 2 مقدار n=3 است و در خط 3 هم شرط برقرار است (0<3)
در خط 4 ، کل تابع بی نام (خط 5 تا 6) را به عنوان مقدار بازگشتی (که از نوع تابع هست) را در متغییر element ذخیره میکند . (پس element از نوع تابع است الان) که در خط 14 ، نوع این متغییر را که تابع است را مینویسد و در ادامه اش آنرا (element را) که تابع است ، فراخوانی میکند که در اینجا یعنی تابع elment که از خط 5 تا 7 است ، اجرا میشود . یعنی در خط 5 مقدار i=1 و در خط 6 ، این تابع مقدار [1]t را برمیگرداند را که 10 است را برمیگرداند و در خط 14 چاپ میکند
برای اجرای بار دوم هم که معلوم است چون تابع ایتریتور فراخوانی شده ی مان در خط 13 ، یک آرگومان دارد و همیشه آرگومان اول ثابت است ، پس دوباره دقیق به همان نحو قبلی ، تابع ایتریتورمان تکرار میشود . این بار مقدار i مان برابر 1 است . وقتی که در خط 14 تابع element فراخوانی میشود ، مقدار i در خط 5 برابر 2 و این تابع ، مقدار 20 را برمیگرداند و به همین منوال تا تعداد آرایه ها که 3 تاست ، تکرار میشود و مقادیرشان را چاپ میکند
در این مثال متغییر i از تابع بیرون کشیده شد وگرنه هر بار مقدارش 1 میشد و هر بار اولین عضو آرایه ی t را برمیگرداند. جای شرط هم عوض شد وگرنه مقدار 4 امین عضو از آرایه ی t را که مقدارش nil بود را برمیگرداند و باعث ارور میشد
اگر در خط 14 ، این کد را جایگزین میکردیم :
چون تابع بازگشتی (تابع بی نام) اجرا نمیشد ، پس مقدار i افزایش نمیافت و i همیشه برابر 0 می بود در تکرار دفعه ی بعد پس تابع تا ابد تکرار میشدکد:Dialog.Message("Notice", type(element), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
اگر آرگومان تابع ایتریتور list_iter در بالا ، 2 آرگومان میداشت و مقدار هر دو آرگومان مثل هم بودند ، بدیهیست که طبق نکاتی که گفته شد ، در اجرای بار دوم به بعد ، مقدار آرگومان دوم ، همان تابع بازگشتی بی نام است . مثلا به این گونه باشد (که چون دفعه ی دوم ، مقدار h ، تابع است در خط دوم ، دفعه ی دوم ، ارور میدهد) :
3)کد:function list_iter (t,h) Dialog.Message("iterator function", h[1], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); local n = #t if i < n then return function () i = i + 1 return t[i] end end end i=0 t = {10, 20, 30} for element in list_iter,t,t do Dialog.Message("Notice", type(element), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
در خط 13 در حلقه ، تابع Init با ویرگول نشانه گذاری شد پس یعنی کل تابع Init به عنوان تابعایتریتور استکد:function Init(a,b) b=b+1 a=a-1 function Sec(a,b) ABC = a+b+5 if ABC<150 then return ABC,a,b end end return Sec(a,b) end for One,Two,Three in Init,10,20 do Dialog.Message("Notic", "ABC : "..One.."\na : "..Two.."\nb : "..Three, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
همانطور که قبلا هم آشنا شدید با قضایا ، مختصرا توضیح داده میشود . a=10 و b=20 به تابع ایتریتور ارسال میشود. در خط 10 ، تابع داخلی اش فراخوانی میشود (اجرا میشود) . که در نتیجه مقدار ABC=35 و a=9 و b=21 میشود و به ترتیب این مقادیر به متغییرهای One و Two و Three ارسال و در آنها ذخیره میشوند . در اجرای بعد ، آرگوما دوم برابر اولین خروجی تابع (One) میشود پس a=10 و b=35 میشود . دوباره در خط 10 تابع داخلی با مقادیر داده شده به عنوان آرگومان، اجرا میشود و این ماجرا تا وقتی که شرط تابع داخلی نقض نشود ، ادامه دارد
همین مثال را میشود بصورت زیر نوشت :
4)کد:function Sec(a,b) ABC = a+b+5 if ABC<150 then return ABC,a,b end end function Init(a,b) b=b+1 a=a-1 return Sec(a,b) end for One,Two,Three in Init,10,20 do Dialog.Message("Notic", "ABC : "..One.."\na : "..Two.."\nb : "..Three, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
این مثال شباهت زیادی به اولین مثالی دارد که گفته شد .کد:function equal(iteratorMaxCount,currentNumber) if currentNumber<iteratorMaxCount then currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end function squares(iteratorMaxCount) return equal,iteratorMaxCount,0 end for i,n in squares(3) do Dialog.Message("Notice", "i : "..i.."\nn : "..n, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
در خط 12 در حلقه ، squares بصورت عادی فراخوانی میشود پس تابع ایتریتورمان نیست . مقدار 3 به آرگومان تابع squares ارسال میشود که خود این تابع ، تابع ایتریتور را با نام equal برمیگرداند و فراخوانی میکند مقدار آرگومان اولش همانطور که مشخص است ، مقدار iteratorMaxCount که 3 تعیین شده و آرگومان دوم هم که 0 هست (یعنی iteratorMaxCount=3 و currentNumber=0) . این بدیهی و مشخص است چون تابع ایتریتور از اینجا مشخص و فراخوانی شد (منظور مقدار بازگشتی همین تابع یعنی خط 9 هست) پس در دفعات بعد آرگومان های تابع ایتریتور از همینجا مقدار دهی و تغذیه میشوند
در خط دوم چون شرط برقرار است ، پس تابع ایتریتور ، در اولین مقدار بازگشتی اش currentNumber را که در حال حاضر برابر 1 است را برمیگرداند که در متغییر i در خط 12 ذخیره میشود و در دومین مقدار بازگشتی اش همین متغییر به توان 2 را که باز هم مقدارش 1 میشود را باز میگرداند که در متغییر n ذخیره میشود .
در اجرای بار دوم ،همانطور که گفته شد ، تابع از خط 9 که فراخوانی تابع ایتریتور از آنجا شکل گرفت ، مقداردهی میشوند و همانطور که قبلا گفته شده بود اولین مقدار خروجی تابع ایتریتور یعنی مقدار متغییر i که فعلا 1 است در دومین آرگومان ارسالی که قبلا در خط 9 مقدار 0 بود ، جایگذین میشود و مقدار اولین آرگومان که iteratorMaxCount و برابر 3 بود تا آخر ثابت میماند . پس این دفعه ، مقدار currentNumber=1 میشود و با این اوضاع i=2 و n=4 میشود .
همین منوال را در اجرای بعدی پیش بروید متوجه خواهید شد که i=3 و n=9 میشود و در دفعه ی بعد دیگر شرط تابع ایتریتور نقض میشود
تکرار آرایه ها با استفاده از توابع next و pairs به عنوان تابع ایتریتور :
همانطور که میدانید ، میتوان با توابع اصلی لوا ، تمام مقادیر و شماره ی عضوها حتی عنصرها (key ها) را با این دو تابع فراخوانی کرد . این دو تابع زمانی چاره گشایی میکنند که در آرایه ای از key استفاده شده باشد . آرایه ی زیر را در نظر بگیرید :
به x و a و y در آرایه ی بالا ، key میگویند و 54 و "hello" و "word" مقادیرشان هستندکد:array={x=54,a="hello",7590,y="word"}
بدست آوردن key ها یعنی x و a و y بصورت عادی بدون استفاده از توابع next و pairs حداقل میشود گفت کار بسیار سخت و یا نشدنی ایست
این توابع ، وقتی به عنوان تابع ایتریتور استفاده شوند (یعنی در حلقه ی for) ، همه ی مقادیر و نام عنصرها (key ها)ی آن آرایه را برمیگردانند تا به عضو nil یا پوچ یا تعریف نشده برسند که با برگرداندن nil ، حلقه از اجرا بازمیایستد
1) تابع next :
قبل از گفتن این تابع به عنوان تابع ایتریتور (که در حلقه ی for بکار برود) ، بصورت تابع معمولی توضیح دهیم . این تابع 2 ورودی (آرگومان) میپذیرد و 2 خروجی (مقدار بازگشتی) میدهد . اولین آرگومانش ، اسم تابع مورد نظر است و دومین آرگومان آن که دلخواهی ست ، شماره ی آن عضوی از آرایه که عضو بعدی اش را میخواهیم میدهیم . خروجی هم مقدار بازگشتی اول اش ، شماره ی عضو بعدی ای که در آرگومان دوم قرار دادیم را برمیگرداند و مقدار بازگشتی دوم اش ، مقدار شماره ی بعدی شماره ای که در آرگومان دوم داده بودیم . مثال :
در خط دوم ، اولین آرگومان تابع next را اسم آرایه ی مورد نظرمان که t است را میدهیم . حالا چون در دومین آرگومان اش عدد 2 گذاشتیم پس در اولین مقدار بازگشتی اش که x باشد ، شماره ی عضو بعدی 2 که 3 است (یعنی سومین key تابع t که 3 است) را برمیگرداند و در دومین مقدار بازگشتی اش که y باشد ، مقدار عضو بعدی 2 (یعنی مقدار عضو سوم از آرایه ی t) که 65 است را برمیگرداندکد:t={32,28,65} x,y=next(t,2) Dialog.Message("Notice", x.."\n"..y, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
دومین آرگومان تابع next دلخواهی است (اگر در توضیحات سایت اصلی این تابع next هم دقت کنید در دومین آرگومان اش علامت کلوشه [ ] گذاشته است که این علامت یعنی آن آرگومان دلخواهی است گذاشتن و نگذاشتن اش) و اگر این آرگومان را مقداردهی نکنیم ، برابر nil بصورت پیش فرض میشود و اگر nil شود ، اولین شماره ی عضو (key) و اولین مقدار آرایه را برمیگرداند
حالا تابع next به عنوان ایتریتور :
در مثال بالا ، در خط دوم ، چون با ویرگول فراخوانی شد تابع next پس نشان دهنده ی این است که این تابع هر چه که هست ، کل این تابع هر بار تکرار میشود . آرگومان اولش اش که معلوم بود و آرگومان دوم اش چون دلخواهی بود و فرض بر این است که از اولین عضو آرایه فراخوانی کنیم ، پس با قرار ندادن (که همان nil قرار دادن آرگومان دوم است) ، از اولین عضو آرایه فراخوانی میشود . k و v هم مثل مثال بالا مقادیر بازگشتی است که توضیح داده شدکد:array={x=54,a="hello",7590,y="word"} for k,v in next,array do Dialog.Message("Notice", "key : "..k.."\nvalue : "..v, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
2) تابع pairs :
به عنوان ورودی ، فقط اسم آرایه را میگیرد و خروجی اش دقیق مثل تابع next است چون این تابع وقتی فراخوانی شود ، تابع next را برمیگرداند (یعنی بصورت غیر مستقیم تابع next مان که به عنوان ایتریتور بود را برمیگرداند پس باید با علامت آرایه که پرانتز بود ، آرگومان اش مقداردهی شود) :
با تشکر از تمام بچه های گل انجمن پرشین کدرز و کاربران بقیه ی انجمن ها که در آموزش سهیم بودند (ممنون میشم به نیت کلیه ی امواتی که در این آموزش سهیم بودند ، فاتحه ای قرائت بفرمایید)کد:array={x=54,a="hello",7590,y="word"} for k,v in pairs(array) do Dialog.Message("Notice", "key : "..k.."\nvalue : "..v, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end






پاسخ با نقل قول

Bookmarks