PDA

مشاهده نسخه کامل : آموزش متنی و نوشتاری Autoplay Media Studio



SajjadKhati
04-08-14, 15:23
به نام خدا

سلام

Only the registered members can see the link


در این تاپیک ، آموزش های متنی AMS گذاشته میشه
لطفا برای دانلود فیلم آموزش یا سئوال و پرسش درباره ی این نرم افزار ، در این تاپیک (اینجا کلیک کنید) (Only the registered members can see the link) بحث کنید

SajjadKhati
04-08-14, 23:20
برای نام گزاری متغییرها ، هیچ وقت از تیکه ای از کد استفاده نکنین.

مثلا تو کد Page.StartTimer ، اگه تو خط های قبل اش یا تو رویداد های دیگه (بسته به رویداد داره که اجرا شده باشه کداش یا نه) ، متغییری بنام Page تریف کنین که دقیقا هم نام یه تیکه ی اول از کدمون که Page.StartTimer بود بدیم و فرضا اگه این متغییر Page رو برابر یه عدد یا رشته که مثلا اینجا میخام بگیرم برابر رشته ی علی ینی ali"=Page بگیرم و تو خط بدش کد Page.StartTimer رو بزارم ، همونطور که میدونین هر جا اسم Page بیاد ، بجاش مقدارش که تو اینجا ali بود رو میزاره و چون اون کد هست و اگه کوچیکترین دستگاری ای بشه ، قات میزنه Only the registered members can see the link پس ارور میده . ینی تو اینجا اگه اینجوری بنویسیم کد رو :



Page = "ali"
Page.StartTimer(1000, 10);



کدمون اینجوری جایگزین میشه و بجای تیکه ی اولش که Page بود ، مقدار متغییر مون که ali بود جایگزین میشه :



ali.StartTimer(1000, 10);



. چون کد ali.StartTimer واسه نرم افزار نامفهومه پس موقه اجرا ، ارور میده

یادتون باشه من گفتم اگه دقیق مثل اون یه تیکه ی کدها (حالا قسمت چپ کد که Page بود یا راستش که StartTimer بود) اما همونطور که میدونین که این نرم افزار برای تریف متغییر ، به حروف بزرگ و کوچیک حساسه پس اگه اون متغییر Page رو بصورت page اسم گزاری کنین ، ارور نمیده چون این دو تا رو به عنوان دو تا متغییر جداگانه میشناسه نرم افزار

SajjadKhati
04-08-14, 23:29
Registery :

برای قسمت registery ، هر جا کلمه ی key رو دیدی ، منظورش پوشه های سمت چپ تو رجیستری هه و هر جا کلمه ی value Name رو دیدی منظورش همون اسم value یا همون اولین ردیف تو ستون سمت چپ هست بنام Name هه و هر جا کلمه ی value خالی یا data رو دیدی ، منظورش مقدار value که همون آخرین ردیف تو ستون سمت راست بنام data هه ، هست
تذکر : نوع value اگه از نوع REG_SZ باشه ، به عنوان رشته ذخیره میکنه و اگه از نوع REG_DWORD باشه از نوع عدد اما موقع خوندن و برگردوندن ، همه رو از نوع رشته میخونه و برمیگردونه

SajjadKhati
04-08-14, 23:32
قضیه ی return (کلمه ی کلیدی return) :


return معمولا تو تابع استفاده میشه و اگه برابر یه مقدار شد ، اگه تابع فراخونی بشه ، اون مقداری که تو return دادیم رو برمیگردونه . مثلا :



n=0
function num(n)
if n==0 then
return 1
end

Dialog.Message("Notice", num(n), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



در اینجا تو خروجی ، num(n) همون مقدار return ای هست که برابر 1 بود ینی تو خروجی نوشته میشه 1

مثلا فاکتوریل رو میخایم با این روش حساب کنیم :




n = Input.GetText("Input1");
n = String.ToNumber(n);


function factorial(n)
if n == 0 then
return 1
else
return n * factorial(n - 1)
end
end
Dialog.Message("Notice", factorial(n), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



تو این دستور از ورودی اینپوت مقداری که وارد شدش رو میگیره و اگه برابر 0 بود (تو ورودی اینپوت عدد 0 نوشته شده بود ) ، تو خروجی factorial(n) ، 1 میشه ینی مینویسه تو خروجی 1
و اگه هر عدد دیگه ای بود ، مثلا اگه بود 3 ، خود اون عدد ضربدر factorial یکی از خودش کمتر حساب میشه ینی اینجوری میشه :
3*factorial(2)
چون تابع factorial دوباره فراخونی شد پس دوباره این تابع اجرا میشه اما این بار با پارامتر یکی کمتر از خودش ینی n=2 ینی در واقع خودش از داخل خودش ، فراخونی میکنه و صدا میزنه تابع خودش رو ینی در واقع همون کار تابع بازگشتی رو میکنه
باز چون n=2 هه ، باز هم این قسمت else اجرامیشه ینی
n * factorial(n -1)

این بار که n=2 هه پس میشه :
3*factorial(2)
خود factorial(2) که مقدار باز گشتی بود دوباره فراخونی میشه و میشه :
2* factorial(1)
ینی در واقع تا حالا شد :
3*(2* factorial(1))
دوباره تابع factorial(1) که اینبار مقدار n برابر 1 هه اجرا میشه و همه ی قبلی ها مثل بالا ضرب میشن در factorial(1) :
factorial(3*(2*(1)))
در اینجا ، تا factorial(1) محاسبه میشه که میشه : factorial(0)*1 و چون factorial(0) مخالف شرط اول تابع هه ، پس فقط تا 1 ضرب میشه و factorial(0) که میشد factorial(-1)*-1 اجرا نمیشه
پس :
3*2*1=6
پس فاکتوریل 3 که میشه 6 بدست اومد

SajjadKhati
04-08-14, 23:34
دستور حلقه ی تکرار while :

اگه ما مثلا بنویسیم :




i=1
while i==5 do
Dialog.Message("Notice", "Your message here.", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
End


خوب این ملومه که چون i برابر 5 نیست پس دستور شرط اجرا نمیشه (مثلا اگه تو خط دوم میزاشتیم i==1 چون قبلش تو خط اول برابر 1 بود پس شرط دستور اجرا میشد)
اما وقتی بجای خط بالا بنویسیم :



i=1
while i do
Dialog.Message("Notice", "Your message here.", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end



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

SajjadKhati
04-08-14, 23:39
فرا آرایه (MetaTable) :

همون آرایه ی معمولی خودمونه اما میشه عنصرها یا همون عضوهاش رو برابر یه متغییر گرفت یا حتی برابر یه متغییر نگرفت . مثلا آرایه ی معمولی که این جوریه :



Array={10,7,3,"ali"}


فرا آرایه ای که متغییر داره ، این جوری میشه :



Array={x=10,y=7,z=3,w="ali"}


یا فرا آرایه ای که متغییر نداره ، این جوری میشه :



Array = {{10,7,3},"Ali"}


که به این نوع آخر ، آرایه ی چند بعدی هم میگن که یکی از پرکاربردترین و مهم ترین و کارآسون کننده ترین هاست Only the registered members can see the link درباره ی این نوع آرایه یا بهتره بگم فراآرایه ی چند بعدی ، تو صفه ی 2 بحث میکنیم (حتما اگه طالب فراآرایه این ، اون مبحث را پی گیری کنین که خیلی مهمه)

ینی خود عضوهاش رو میشه تو یه متغییر دیگه ریخت و نام گزاری کرد متغییر هاش رو اما تو آرایه ی معمولی ، فقط میشه مقدار داد (نه اینکه متغییر همراش نام برد)
مهم ترین ویژگی فرا آرایه اینه که چون حالت چند بعدی (هر تعداد مثل 3 یا 4 بعدی و ...) هستن ، میشه کار چندین آرایه رو تو یه آرایه انجام داد . منظور از چند بعدی اینه که چندین اندیس (عدد داخل پرانتز آرایه ها) که همون نوعی متغییر هستن میشه براش تعریف کرد پس بجا اینکه چندین آرایه تعریف کرد ، میشه همه ی اون آرایه ها رو (هر چند تا میخان باشن) فقط تو یه فرا آرایه ی چند بعدی خلاصه کرد که کار رو فوق العاده آسون و سریعتر میکنه و علاوه بر اون ویژگی دیگه شم اینه که میشه دو تا فرا آرایه ی مختلف و حتی عضوهای مختلف یه فرا آرایه رو میتونیم روش عملیات ریاضی (جمع و کم و ...) و ... انجام بدیم.
تو فرا آرایه ، حتی میشه بجای یه عضو ، یه آرایه ی دیگه نام گذاری تعریف کرد. (خود اون آرایه هم باز میتونه یه آرایه ی معمولی ( بدون متغییر داخلی ) باشه یا یه فرا آرایه باشه که متغییر داشته باشه)
مثلا اگه آرایه ی معمولی (بدون متغییر) باشه :



a={v={ 7, 1, 30},y=20,z=30}



یا مثلا اگه خودش دارای یه فرا آرایه باشه :




a={v={x=7,y=1,z=30},y=20,z=30}



این جوری ای که گفتم ، دقیق باید همین جوری نام گذاری کرد . ینی مثل حالت عادی باید تعریف کرد دیگه. ینی وقتی حتی میخایم داخل خود یه فرا آرایه ،آرایه یا فرا آرایه ی دیگه ای رو تعریف کنیم ، مثل حالت عادی ای که تعریف میکنیم ، اون آرایه یا فرا آرایه ی داخلی رو هم باید بهش یه نام بدیم . (که در بالا برابر v گرفتیم)

حالا در اینجا فقط اولین عضو فرا آرایه ی a رو براش آرایه و فرا آرایه ی دیگه ای تعریف کردیم . میتونیم عضوهای دیگه رو هم این جوری کنیم

حالا فراخونی فرا آرایه که مهمترین قسمته :

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

مثلا دو تا فرا آرایه داریم که فرا آرایه ی اول اسمش a هست و فرا آرایه ی دوم اسمش b :




a={x=10,y=20,z=30}
b={x=2,y=7,z=5}


خوب حالا مثلا میخایم بگیم که x امین عضو از فرا آرایه ی a که 10 هست رو با x امین عضو از فرا آرایه ی b که 2 هست رو جمع کن ، این جوری فراخونی میکنیم که چون خود عضو های آرایه ، متغییر دارن ، پس باید اسم متغییر هاشون رو برد ینی فراخونی آرایه با روش دوم که تو بالا گفتم پس a.x ینی x امین عضو از فرا آرایه ی a که 10 بود و b.x هم همون x امین عضو از فرا آرایه ی b رو که 2 بود برمیگردونه پس مینویسم :

c=a.x+b.x

ینی 10+2 که میشه 12 رو تو متغییر c ذخیره میکنه و تو کد پایین نمایش میده



Dialog.Message("Notice", c, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



ما میتونیم هر عضو از یه فرا آرایه رو با هر عضوی که دلمون خاست ، عملیات ریاضی انجام بدیم تو مثال بالا مثلا :

C=a.z+b.x

که z امین عضو از آرایه ی a رو که 30 بود با x امین عضو از آرایه ی b که 2 بود رو جمع میکنه که میشه 32 و تو متغییر c ذخیره میکنه
و حتی فقط عضوهای یه آرایه رو عملیات ریاضی انجام بدیم که خوب ملوم بود و لازم به گفتن نبود.

همینطور میتونیم عضوهای یه فرا آرایه رو با عضوهای یه آرایه ی معمولی با هم ، عملیات ریاضی انجام بدیم مثلا :




a={x=10,y=20,z=30}
b={ 2, 7, 5}



a که یه فرا آرایه هست و b هم یه آرایه ی معمولی . طرز فراخونی هر دو تا شونو تو همون اول گفتم . اونی که متغییر نداره ، ینی عضوهای آرایه ی معمولی رو با شماره (ای که داخل کلوشه هه) فراخونی میکنیم و عضوهای فرا آرایه رو چون متغییر داره ، با اسم متغییرش (با نقطه قبل اش) فراخونی میکنیم پس اگه میخایم بگیم اولین عضو فرا آرایه ی a رو که 10 هست میشه a.x و اگه میخایم بگیم اولین عضو آرایه ی b که 2 هست میشه b[1] ینی :



c=a.x+b[1]



که همون 10 رو با 2 جمع میکنه و 12 رو تو c ذخیره میکنه و .. :




Dialog.Message("Notice", c, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



همین طور که همون اول گفتم ، میتونیم داخل یه فرا آرایه ، یه فرا آرایه ی دیگه ای رو به عنوان عضواش تعریف کنیم و با یکی دیگه عملیات ریاضی انجام بدیم مثلا :




a={x={x=7,y=1,z=30},y=20,z=30}
b={x=2,y=7,z=5}



در اینجا ما میخایم بگیم اولین عضو از فرا آرایه ی a که خودش هم یه فرا آرایه ای هه بنام x بگیم که اولین عضو از این فرا آرایه ی x رو که متغییری هست بنام x که مقدارش 7 هست رو با اولین عضو از فرا آرایه ی b که متغییر اش x با مقدار 2 هست رو جمع کنه.
همیشه مقدار بعد از آخرین نقطه تو فراخونی ، اندیس یا همون عضو آرایه هست و قبل از اون آخرین نقطه همه شون میشن اسم آرایه پس ینی فقط کافیه برای فراخونی اولین عضو فرا آرایه x که خودش تو فرا آرایه ی a هست ، کلا به ترتیب اسم هر آرایه و هر متغییری رو که میخای ، بزاری و بین شون نقطه بزاری ینی :
a.x.x
ینی همون مقدار 7 که اولین عضو فرا آرایه ی x هه. ینی x امین عضو از فرا آرایه ی x که خودش تو فرا آرایه ی a هه
یا مثلا a.x.y ینی y امین عضو از فرا آرایه ی x که تو فرا آرایه ی a هه که تو مقدار بالا میشه همون 1
خوب حالا بریم سر اصل مطلب که میخاستیم اون 7 رو با 2 که گفته شد جمع کنیم .2 هم که منظورمون همون اولین عضو فرا آرایه ی b بود میشه b.x
پس میشه :






c = a.x.x + b.x
Dialog.Message("Notice", c, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1


یادتون نره که تو صفه ی 2 پست 14 ، فراآرایه ی چند بعدی رو هم دنبال کنین چون مبحث کاربردی ایه

SajjadKhati
04-08-14, 23:43
نوشتن فراآرایه به روش دیگر و کاربردی تر :

خسته کیه؟
نشنیدم؟
شنیدم بابا ، آروم تر ، کر شدم

خوب گلپسران اساتید ، نگا کنین ، همونطور که میدونین ، آرایه رو به دو روش میشه نوشت . یکی مثل روش بالا که تو پایین هم مینویسم و بلدین :



Array={10,7,3,"ali"}


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



Array = {}
Array[1] = 10
Array[2] = 7
Array[3] = 3


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



a={v={ 7, 1, 30},y=20,z=30}


برا نامگزاری و فراخونی y امین عضو آرایه ی a که 20 هست (**یادتون باشه که تا وقتی که یه عضو را برابر یه متغییر گرفتیم ، مثل همین y ، دیگه گفتن شماره و اندیس برا اون عضو بی معنی هه ینی اینجا نمیششه گفت دومین عضو آرایه ی a برابر 20 هست . باید بگین y امین عضو از آرایه ی a) باید بنویسین a.y که میدونین و اوستایین دیگه ، ینی y امین عضو از آرایه ی a که چن بود؟ 20 بود دیگه. خوب پس فراخونی اش چجوری هه؟ ینی هر جا نقطه اومد ، سمت راست اون نقطه (در اینجا y) زیر مجموعه ی هر چی که سمت چپ نقطه نوشته هه هست (در اینجا a) و ممکنه حتی این زیر مجموعه (در اینجا y ) ، خودش یه آرایه ی دیگه ای باشه (که فلا تو این مثال نیست )

**یادتون باشه که تو فرا آرایه ، اگه عضوی نامگزاری نشه (متغییر براش تعریف نشه و مثل حالت عادی باشه)، به عنوان اولین عضو اون آرایه حساب میشه . مثلا تو مثال :



a={v={ 7, 1, 30},y=20,30}


تو اینجا ، از زیر مجموعه های آرایه ی a ، آرایه ی v که خودش زیرآرایه ی a هسست (ینی واسه خودش یل ای هه و ازدواج کرد و تشکیل خانواده داد ) ینی هر چی که آرایه بود رو کار نداریم . y که براش نام انتخاب شد و عضو نامداری هه (ینی آدم معروف و نامداری هه) تنها بی نام و نشون میمونه اون آخری هه که 30 هست و برخلاف تصور ، اولین بی نام و نشون میشه ، اولین عضو آرایه که a بودش . ینی :



a[1] = 30


میباشد.
مثالای دیگه هم که تو پست بالا کار کردیم و نامگزاری رو بلدین دیگه؟ مثل :



a.v[2] = 1


ینی اینکه دومین عضو از آرایه (یا زیر آرایه) ی v از آرایه ی a که میبینین میشه 1
حالا بریم سر خر ماجرا (نه اسب) ینی که میخایم همون آرایه ی a ی بالا رو یه جور دیگه بنویسیم (که اصل ماجرا گفته شد) :
اول اینو پایین دوباره بنویسم تا تو چش بیاد قشنگ :



a={v={ 7, 1, 30},y=20,30}


دونه دونه از اول انگار میخایم فراخونی کنیم و مثل مثال ساده ای که همون اول همین پست (چند خط اول) گفتم ، هر جا آرایه یا زیر آرایه (منظورم از زیر آرایه عضو های عادی نیستا . مثل v تو مثال بالا هه که زیر آرایه هه) و کلا هر جا علامت آرایه که میدونین {} دیدین ، مثل مثال اول ، قبلش باید تعریف کنین پس من برا نوشتن آرایه ی بالا ، اول آرایه ی a رو دیدم .پس تو خط اول مینویسم:


a = {}

بعد آرایه یا همون زیر آرایه ی v دیدم پس تو خط بعدش باید عضو شو ملوم کنم . مثل همون قضیه ی بالا ینی جایگاه v اینجوری میشه که v امین عضو از آرایه ی a هست و چون خودش آرایه هست باید یه آرایه براش تریف کنم :


a.v = {}

حالا نوبت میرسه به اولین عضو زیر آرایه ی v که 7 هست و چون اولین عضو آرایه ی v هست و عضو اش هم نامگزاری نشدش پس باید با اندیس نامگزاری کنم پس تو خط بعد مینویسم :


a.v[1] = 7

همینطور خط بعد :


a.v[2] = 1

همینطور خط بعد :


a.v[3] = 30

خط بعدش هم که عضو y میشه و دیگه زیر مجموعه ی آرایه ی v نیست پس دیگه v رو لازم نیست بنویسیم و میشه y امین عضو از آرایه ی a ینی :


a.y = 20

خط بعدش هم همونطور که گفته شد ، چون عضوش نام نداره و اولین عضو بدون نام هست ، پس اولین عضو آرایه ی ماقبل خودش که a هست میشه ینی :


a[1] = 30


پس در کل نوشتن اش تو خط های پشت سر هم میشه :



a = {}
a.v = {}
a.v[1] = 7
a.v[2] = 1
a.v[3] = 30
a.y = 20
a[1] = 30


حالا یه مثال دیگه :



Array = {Madrese={5,"Tafrih"},"Ali",Khone={12,Bazar={1,2,3,4,5,6,7,8}}}


جان من اسم متغییرها رو حال کردین؟
اینو توضیح بدم؟ مینویسم ، مثل قضیه ی بالاهه هر کی سئوال داشت ، بگه (کسی نمیاد اینورا که بخونه که سئوال داشته باشه )



Array = {}
Array.Madrese = {}
Array.Madrese[1] = 5
Array.Madrese[2] = "Tafrih"
Array[1] = "Ali"
Array.Khone = {}
Array.Khone[1] = 12
Array.Khone.Bazar = {}
Array.Khone.Bazar[1] = 1
Array.Khone.Bazar[2] = 2
Array.Khone.Bazar[3] = 3
Array.Khone.Bazar[4] = 4
Array.Khone.Bazar[5] = 5
Array.Khone.Bazar[6] = 6
Array.Khone.Bazar[7] = 7
Array.Khone.Bazar[8] = 8


همونطور که میدونین ، 8 تا خط آخر هم میتونین با حلقه ی for تریف کنین :



Array = {}
Array.Madrese = {}
Array.Madrese[1] = 5
Array.Madrese[2] = "Tafrih"
Array[1] = "Ali"
Array.Khone = {}
Array.Khone[1] = 12
Array.Khone.Bazar = {}


for i=1,8 do
Array.Khone.Bazar[i] = i
end


دیدین که همه چی آسون بود
یادتون نره که تو صفه ی 2 پست 14 ، فراآرایه ی چند بعدی رو هم دنبال کنین چون مبحث کاربردی ایه

SajjadKhati
04-08-14, 23:46
فرا آرایه یا آرایه ی چند بعدی :

قبل از همه جا داره یه تشکر حسابی از آقا محمد (سید اهل انجمن ) کنم که واقعا تا آخرین سئوالات من جواب داد. فعلا که جز تشکر خشک و خالی نمیتونم کنم . ان شاء ا... جبران کنم (نمیتونم )
بعد بگم که حتما باید پست فرا آرایه رو که تو صفحات قبل هه بخونین تا این مطالب براتون نا آشنا نباشه و راحت تر یاد بگیرین
همونطور که تو همون پست گفته شد ، مهم ترین ویژگی فرا آرایه اینه که چون حالت چند بعدی (هر تعداد مثل 3 یا 4 بعدی و ...) هستن ، میشه کار چندین آرایه رو تو یه آرایه انجام داد . منظور از چند بعدی اینه که چندین اندیس (عدد داخل پرانتز آرایه ها) که همون نوعی متغییر هستن میشه براش تعریف کرد پس بجا اینکه چندین آرایه تعریف کرد ، میشه همه ی اون آرایه ها رو (هر چند تا میخان باشن) فقط تو یه فرا آرایه ی چند بعدی خلاصه کرد که کار رو فوق العاده آسون و سریعتر میکنه و علاوه بر اون ویژگی دیگه شم اینه که میشه دو تا فرا آرایه ی مختلف و حتی عضوهای مختلف یه فرا آرایه رو میتونیم روش عملیات ریاضی (جمع و کم و ...) و ... انجام بدیم.

تعریف فرا آرایه ی چند بعدی :
همون دقیق مثل فرا آرایه ی قبلی هه اما هر جا داخل فرا آرایه ، متغییر دیدین ، نزارین . همین یَندِک
مثلا تو همون پست قبلی که درباره ی فرا آرایه بود مثال زیر یکیش بود :



Array={v={ 7, 1, 30},y=20,z=30}


برا تبدیل این به چند بعدی ، کافیه هر چی داخل فرا آرایه ی Array ، متغییر میبینین ، حذف کنین ینی کنینش :



Array={{7,1,30},20,30}



فراخونی فرا آرایه ی چند بعدی :

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




ABC = {54,65,3}


وقتی میگین ABC[2] ینی چی؟ نخندین . بال بِنِه بَزِه بَمِرداشون (این تیکه مخصوص زبون خودمه . مازندرانی کی این دور و بره؟ ترجمه نکنه ) الان میگین خوب هر کی میدونه ینی دومین عضو آرایه ی ABC . آری اما این جوری معنی کنین . ینی دومین آرگومان یا ورودی یا همون بعد از دومین ویرگول از آرایه ی ماقبل خودشABC هست . آرگومان هم که ملومه دیگه چیه. هر جا ویرگول شد ، میشه آرگومان بعدی (البته ورودی نمیشه گفت چون باید متغییر دریافت بشه که روش الگوریتم پیاده شه که مخصوص توابع هست). الان تو بالا ، 54 میشه آرگومان اول و 65 که میشه بعد ویروگول میشه دومیش و 3 هم که بازم بعد ویرگوله میشه سومیش (آرگومان حالا میخاد یا عضو هست یا زیر آرایه)
بازم قبل از چیزای دیگه با چن تا دیگه از فرا آرایه ی چند بعدی آشنا شین . مثال ها :


Array={{{"ali",54,"Salam"},{true,789,"Payam"}},{4,Moteghaeir,75},{985},"BedoneZirArrayeh",25}

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


Array={{7,1,30},20,30}

عدد 1 فراخونیش چجوریه؟
داخل فرا آرایه ی Array ، زیر آرایه ای که عدد 1 توش هستش تو آرگومان اول هه ینی اولین آرگومان آرایه ی ماقبل اش که Array هست رو مینویسیم که تا اینجای کار میشه :


Array[1]

حالا دیگه داخل این زیر آرایه ، فقط اعضاش میمونه که چون 2 رو میخایم عدد 2 رو تو اندیس بعدی مینویسیم ینی کلا میشه :


Array[1][2]


تا اینجا دید یو آندرستند (تود)؟ ندید یو؟ خاهید دید یو
مقدار این تو زیر چی میشه؟ :


Array[3]

خوب این که دیگه ملومه فقط آرایه هست نه فرا آرایه یا چند بعدی . چون یه دونه اندیس فقط داره و آخرین اندیس هم شماره ی عضو رو ملوم میکرد. از این حالت هم متوجه نشین ، از همون حالت قبل میریم ینی داخل فرا آرایه ی Array ، سومین آرگومان اش چی میشه تو اینجا هم آرگومان منظورش چیه؟ منظورش همون سومین عضوه . اولین آرگومان اش که همون زیر آرایه میشه و دومیش که 20 و سومیش 30 هه که ملومه
من برم شام بخورم بیام
اگه گفتین چی داشتیم؟ دلتون خربزه

2) تو فرا آرایه ی


Array={{{7,1,30},{780}},{20,45},50,80,95,{451,313} }

این فراخونی زیر برا کدوم عدده؟ :


Array[1][2][1]

و همینطور به ترتیب عددهای 313 و 30 و 780 و 20 چجوری فراخونی میشن؟

اول قسمت الف ینی فراخونی برا کدوم عدده رو بگم . گفت Array[1] منی این چی بود؟ این بود که اولین آرگومان داخل آرایه ی ماقبل خودش ینی اولین آرگومان داخل آرایه ی Array . خوب . داخل آرایه ی Array اولین آرگومان اش کدومه؟ چون داخل آرگومان اول ، چند تا زیر آرایه هست ، خوب ملومه دیگه ینی منظورش کل زیر آرایه هایی هه که تو آرگومان اول تعریف و پرانتزش باز و بسته شد . پس منظورش تا اینجا میشه :


{{7,1,30},{780}}

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


{7,1,30}

این آرایه و دومیشم که میشه :


{780}

پس منظورش اینه و آخری هم که شماره ی عضوه که ملومه تو این آرایه ، یه عضو بیشتر نیست پس جواب یا مقدار فراخونی :


Array[1][2][1]

میشه 780

جواب سئوال قسمت ب برا عدد 313 :
عدد 313 داخل کدوم زیر آرایه هست؟ ینی شماره ی آرگومان اون زیر آرایه ، چنده؟ اولین آرگومان فرا آرایه ی Array که میشه


{{7,1,30},{780}}

دومیشم که میشه


{20,30}

سومیش میشه 50 و چارمیش 80 و پنجمیش 95 و شیشمیش که میشه همون زیر آرایه ی مورد نظر ما که


{451,313}

هست . پس اولین اندیس اش میشه 6 . حالا 313 چندیمین عضو این زیر آرایه هه؟ دومی دیگه پس فراخونیش میشه :


Array[6][2]



برای عدد 30 :
برا اینکار هم از داخل فرا آرایه ی اصلی شرو کنین و هر جا که به آرگومان مورد نظر رسیدین ، عدد اون رو داخل اندیس بنویسین و همینطور برا آرگومان بعدی ، دوباره تو اندیس کناریش و ... تا به شماره ی عضوش برسین
برا عدد 30 ابتدا، به اولین آرگومان برخورد میکنیم که


{{7,1,30},{780}}

هست پس اینجا تو اولین اندیس عدد 1 را مینویسیم . برا اندیس بعدی ، بازم داخل این ، عدد 30 رو تو آرگومان اول میبینیم که بازم زیر آرایه هست . که میشه این آرایه :


{7,1,30}

پس بازم اندیس 1 میدیم و برا اندیس بعدی هم میمونه شماره ی عضو عدد 30 که میشه 3 پس کلا میشه :


Array[1][1][3]


عدد 780 که جوابش تو قسمت الف داده شد . میمونه عدد 20
این زیر آرایه


{20,30}

چندمین آرگومان Array هه؟ خوب ملومه دومی. پس اولین اندیسش میشه 2 و اندیس بعدی هم که شماره ی عضو عدد 20 رو باید بنویسیم که اولین عضوش هست پس میشه :



Array[3][1]

بازم دیدید؟ ینی دید یو آندرستودینگا؟ هاتی اینگا پاتی اینگا؟ منم هنگ کردنینگا والا . بده یه قرن نوشتن ، کروم قات زد ، اون وری هم تو انجمن ذخیره نشد ، مردم دارم دوباره مینویسم



تعریف فرا آرایه ی چند بعدی به روش کاربردی تر :
خوب گلپسران میدونین که تعریف آرایه دو روش داره که اون روش کاربردی تر که خیلی راحت تره و دقیق موقه فراخونی هم ازش استفاده کردیم گفته نشد که گفته میشه
قبل از توضیح ادامه ی این مطلب ، دقیق تو فرا آرایه ای که چن صفه پیش گفته شد و اگه میخاستین تعریف کنین ، همه ی اعضاشو (که اونجا اعضاشون میشه گفت متغییر بودن و با نقطه از هم جدا میشدن) باید به عنوان آرایه تعریف میکردین ینی علامت آرایه که {} هست میزاشتین ، اینجا هم همینطوره . مثلا تو اونجا برا تعریف Array.AddedSoftware.Patch باید اینجوری مینوشتین :


Array={}
Array.AddedSoftware={}
Array.AddedSoftware.Patch={}

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


Array[4][1][4][2]

اون آخری اندیس که 2 هست که شماره ی عضوه که هیچ چی (ینی اون باید برابر مقدار که یا عدده یا رشته یا بولین حتما گرفته بشه وگرنه nil یا پوچ میشه مقدارش) و منظورم از هیچ چی اینه که برا اون علامت آرایه لازم نیست بزاری (چون صوبت فلا سر علامت آرایه گرفتنه) و برا دونه دونه اندیس های قبلیش باید آرایه تعریف کنین مثل مثال بالا ینی باید حتما قبلش اینو بنویسین :


Array={}
Array[4] = {}
Array[1] = {}
Array[4] = {}


حتما هم لازم نیست همه ی زیر آرایه ها تعریف بشن یا همه ی اعضاشون . مثلا میتونیم بدون اینکه زیر آرایه ی اول رو تعریف کنیم ، بپریم رو دومی و دومی رو تعریف کنیم ولی یادمون باشه هر کدوم رو که تعریف نکنیم ولی فراخونی کنیم (چه عضو یا چه زیر آرایه) ، مقدارش برابر nil میشه که ملومه مثلا :



Array[13][1]=30

میتونیم مستقیم فقط همین رو تعریف کنیم بدون اینکه زیر آرایه یا عضوهای دیگه رو تعریف کنیم اما بقیه ی اعضا یا زیر آرایه تا تعریف نشن ، برابر nil میمونن که ملومه . منظورم اینه که تو بالا که Array[4] تعریف شد ، حتما نباید Array[1] و Array[2] و Array[3] قبل اش تعریف بشن و میتونیم فقط آرایه برا بالا رو تعریف کنیم بدون اینکه اروری داشته باشیم ینی برا تعریف و مقدار دادن فرا آرایه ی بالا ، قبلش فقط کافیه این رو تعریف کنیم که تو بالا هم گفته شد :


Array={}
Array[13] = {}


در این مثال بالا هم فقط سیزدهمین زیر آرایه از فرا آرایه ی Array تعریف شد که اولین عضوش برابر 30 هه
مِرِه خو گِج هاکرده (نکِردِه) مازندرانی ندارین بین تون ها . آ راستی اوستا محسن هستا . لو میرم
ادامه اش شد واسه امروز . اولین پستی هه که از روز قبل فرا رفت


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


Array[2][2][1] = "Ali"
Array[1][1][1] = "hasan"

با استفاده از حلقه ی for ، چجوری قبلش آرایه براشون تعریف میکنیم؟ دیگه این قد که کاری نداره و توضیح دادن نمیخاد که . ها؟ هم نحوه ی ساخت آرایه گفته شد و هم این حلقه رو بلدین پس مستقیم میریم سر وقت جواب :


Array={}
for i=1,2 do
Array[i] = {}
Array[i][i] = {}
end


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


Array[1][1] = "Ali"
Array[1][1][1] = "hasan"

همون واسه اولی ارور میده که اسم "Ali" هست . چرا؟
چون شما حلقه ی for رو که بررسی کنین ، میبینین این ساختار از آرایه رو تعریف کردین (رشته ی "SomeThing" فقط برا پر کردن عضو هست . تعداد عضو هم میتونه فرق داشته باشه اما من دلخواه ، یه دونه گرفتم برا هر کدوم) :


Array = {{{"Somthing"},"Somthing"},{"Somthing",{"Somthing"}}}


خوب حالا اگه مقدار دهی ای که قبلا کرده بودین ینی :


Array[1][1] = "Ali"

رو بررسی کنین ، میبینین که تو ساختار که تعریف کردین ، تو آرگومان ، بجای اون قرمزه ، یه زیر آرایه تعریف کردین اما موقه مقدار دهی ، اون رو زیر آرایه در نظر نگرفتین و مستقیم بهش یه مقدار دادین ینی این جوری گفتین (به پرانتز قسمتی که قرمزه تو بالا و پایین خوب دقت کنین) :


Array = {{"Somthing","Somthing"},{"Somthing",{"Somthing"}}}

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



Array = {}
Number = 1
for i=1,3 do
Array[i] = {}
for x=1,3 do
Array[i][x]=Number
Number = Number + 1
end
end


این الگوریتم هم ملومه دیگه . یه فرا آرایه ی 2 بعدی هه که 3 در 3 هست ینی 3 تا آرایه یا زیر آرایه داخلشه که هر کدومشون 3 عضو دارن و دقیق میشه این رو تعریف کردین :



Array = {{1,2,3},{4,5,6},{7,8,9}}


مِردال شدیم رفتیم نشدیم رفتیم طومار غضنفر علی شاه شد
ان شاء ا.. که یاد گرفته باشین و بازم سر آخر کاری نمیتونم برا سید مون کنم جز یه تشکر خشک و خالی
به نیت اموات خودمون و خودتون و هر کسی که تو کلیه ی این آموزش ها سهیم اند هم یه صلوات بفرستین ، ممنون میشم

SajjadKhati
04-08-14, 23:52
دستورnext برای گرفتن عضو بعدی آرایه:

با این دستور (کلمه ی کلیدی نیست) ، میتونیم مقدار متغییر (یا همون عضو) بعدی یه آرایه رو بدست بیاریم. ساختارش دقیقا به این صورت هست (یه قسمت رو ننویسی ، نمیشه) :

باید برای ریختن متغییر بعدی با دستور next ، براش دو تا متغییر تعریف کنی (برابر دو متغییر دیگه بگیری) که هر کدوم با علامت ویرگول جدا میشن . و سر آخر ، اسم دومین متغییر که سمت راست ویرگول بود رو نام ببری . طرز استفاده شم اینجوریه که بعد از اینکه کلمه ی next رو نوشتی ، اون آرایه ای که مد نظرته و میخای متغییر یا عضو بعدی شو بدونی ، اسم شو مینویسی و بعد یه ویرگول میزاری و شماره ی اون آرایه ای که میخای عضو بعدی شو بدونی رو مینویسی مثلا اگه بنویسی 2 ، عضو بعدی اون آرایه رو میگه ینی سومین عضو اون آرایه رو میگه.
یه مثال :



array = {"ahmad","hasan","rahmat","rahman",4}
First,Secand=next(array,2)
Dialog.Message("Notice", Secand, MB_OK,
MB_ICONINFORMATION, MB_DEFBUTTON1);



که آرایه ی بنام array مون تو اینجا ، 5 عضو داره که 4 تای اولی ، رشته و پنجمین عضوش ، عدد هست
همونطور که گفتم ، در خط دوم ، برای قضیه ی next که عضو بعدی اون آرایه ای رو که بهش بگیم ، بهمون برمیگردونه رو باید تو دو تا متغییر ریخت و با ویرگول جدا کرد و متغییر سمت راست رو فراخونی کرد تا دستور next کار کنه. پس اولین متغییر رو تو اینجا اسمشو گرفتم First و با یه ویرگول ، اسم دومین متغییر اش رو Secand گرفتم. ساختار next هم که گفتم بعد از نوشتن next ، یه پرانتز باید باز کرد که اولین قسمتش اسم آرایه ای رو که قبلا تعریف کرده بودیم و تو دومین قسمت اش (که با ویرگول جدا میشن) ، شماره ی اون عضوی از آرایه که عضو بعدی شو میخایم رو باید نوشت و چون اینجا نوشته شد 2 ، پس عضو بعدی آرایه ینی عضو سوم آرایه ی array که "rahmat" هست ، داخل دومین متغییری که بعد ویرگول نام برده شد (در اینجا متغییر Secand ذخیره و برگردونده میشه)
خط آخر هم که دیگه معلومه و متغییر Secand که سومین عضو آرایه ی array هست ، فراخونی شد که گفتم دیگه و "rahmat" رو برمیگردونه.
تذکر : اگر هم تو قسمت یا همون ورودی دوم دستور next ، کلمه ی nil رو بنویسیم ، اولین عضو اون آرایه که معلوم کردیم ، مقدارش برامون برگردونده میشه (نه اینکه یکی بعد از اولین عضو ینی دومین عضو آرایه برگردونده شه ها . تو این nil دیگه اینجوری نیست . خود اولین عضوش رو برمیگردونه. خود nil هم به منی هیچ و پوچ هست دیگه )
ینی اگه تو بالا خط دوم مینوشتیم :




First,Secand=next(array,nil)



وقتی متغییر Secand فراخونی بشه ، اولین عضو از آرایه ی array که "ahmad" هست ، برگردونده میشه اما خوب ملومه که اگه بنویسیم :




First,Secand=next(array,1)


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




First,Secand=next(array,2)


بود ، داخل متغییر First ، عدد بعدی عدد 2 که عدد 3 هست ، ذخیره و برگردونده میشه. (نه سومین عضو آرایه ای که ملوم شد)
اما اگه فقط یه دونه متغییر برا دستور next تعریف شده باشه ، فقط مثل همین خط بالا که گفتم ، عدد بعدی اون عددی که نوشته شد رو برمیگردونه (نه عضو بعدی آرایه رو) . مثلا :




abc =next(array,2)



الان دیگه متغییر abc ، مثل چن خط بالا که گفتم ، عدد بعدی 2 که همون 3 هست رو برمیگردونه (پس برا اینکه عضو بعدی آرایه برگردونده شه ، باید متغییر دوم (سمت راست ویرگول) رو فراخونی کرد.

این دستور معمولا توی تولید مثل (ریختن یه آرایه تو یه آرایه ی جدید دیگه) کاربرد داره

SajjadKhati
04-08-14, 23:55
توضیح یه الگوریتم برای تکثیر یه آرایه (نیازمندی هاش اول تمرین و بعد دیدن فیلم ها + پست های آموزش قبلی هه) :

ینی میخایم یه آرایه رو کپی کنیم و علاوه بر اینکه خود اولیش باشه ، عضوهاشو تو یه آرایه ی دیگه بریزیم
خوب اول بگم که اساتیدای گل مهندسین که شاید مثلا یه مسئله به کارمون در ظاهر نیاد مثل همین تکثیر آرایه . اما در باطن و برای حل مسائل دیگه که مواجه میشین ، روش و الگوریتم یه مسئله خیلی به کار آدم میاد. ینی چیزی که مهمه الگوریتم و روش حل مسئله هست هر چند اون مسئله به درد تون نخوره در ظاهر . پس پیشنهادم اینه که اگه میخاین کد نویسی و حتی برنامه نویسی تون خوب شه ، تا میتونین الگوریتم هایی که هر کی به کار برد رو روش فکر کنین برا یاد گیری . هر چند اگه اون طرف از یه زیان دیگه استفاده میکنه و شما از یه زبان دیگه. تا اینجا را که داستان سرودم Only the registered members can see the link بریم سر اسب مطلب Only the registered members can see the link
بازم لازمه قبل آموزش تکثیر آرایه ، یه چن تا نکات ابتدایی رو که تو فیلم هم گفته شد یاد آوری کنم:
نگا کنین همونطور که تو فیلم هم گفته شد ، طرف راست مساوی میشه مقدار متغییر (هر چند اسم متغییر برده شه) و طرف چپ مساوی میشه خود متغییر که اون مقدار توی همین (طرف چپ) ذخیره میشه. اگه بخام مثال همون فیلمه رو که گفتم بزنم ، طرف راست مساوی کار سیب و پرتغال رو میکنه Only the registered members can see the link و طرف چپ مساوی همون جعبه ای هست که این سیب و پرتغال رو میریزین توش . پس این سیب یه مقداری هست تو جعبه گزاشتیم . واسه این میگیم متغییر که میتونیم بجای سیب ، توش پرتغالم بزاریم Only the registered members can see the link نارنگی هم بزاریم ، تامسون و این جور چیزا که الان کسی طرفش نمیره . این قد گرونن Only the registered members can see the link ینی توی اون جعبه هر چی که دوس داریم میتونیم بزاریم و چون هر چی که عشق مون کشید رو میتونیم تو اون جعبه بزاریم ، مثلا سیب ، پس محتوای و مقدار داخل جعبه ، دست خودمونه که تغییرش بدیم . واسه همین بهش میگن متغییر . اما تو یه جعبه ، انواع مقدارهای مختلف نمیشه گزاشت ینی نمیشه هم سیب گزاشت تو یه جعبه و هم پرتغال و ... . تو یه جعبه یا سیب باید باشه ، یا پرتغال و یا ... . میدونبار که تا حالا رفتین و دیدین دیگه Only the registered members can see the link
یا مثلا لیوان میشه متغییر ما (سمت چپ مساوی) و چیزی که تو لیوان هست یا حالا آب هه یا شیر هه یا آب نباته Only the registered members can see the link (سمت راست مساوی) میشه مقدار و محتوای متغییر ما
بعد هر کی از این به بعد این جعبه هه رو دست شما دید و گفت این چیه ، چی میگین؟ نمیگین جعبه هه که (خوب طرف کور که نیست میدونه این جعبه هه Only the registered members can see the link) منظورش محتوای (یا همون مقدار) این جعبه هه که داخلش چیه؟ که مثلا شما میگین سیب هه ینی محتوای جعبه رو میگین (سمت راست مساوی) که در واقع همون قضیه ی فراخونی میشه

متغییر هم همین طوره. یه مقدار یا محتوا هست (که شیشصد بار گفتم سمت ... Only the registered members can see the link) و یکی هم جعبه که همون متغییر ماست (سمت ...)
اگه یه متغییر هم فراخونی بشه ینی طرف سمت راست مساوی یه متغییر دیگه اسمش برده بشه یا تو ورودی یه دستور اسمش برده شه ، مثل قضیه ی همون پرسیدن محتوای جعبه هه رو میمونه که محتوا و مقدار متغییر برگردونده میشه
الان مثلا اگه بگیم :



var = 5



الان متغییر ما که اینجا سمت چپ مساویه اسمش var هست و (اسمش هر چی دلمون خاسته) مقدارش میشه سمت راستش ینی 5
این مقدار همون سیب و پرتغال بود که باید یکی توش باشه ینی از یه نوع مقدار باید باشه ینی مقدار ما باید با از نوع عددی باشه یا رشته ای که اینا ها تو فیلم گفته شد و اینجا از نوع عددیه که 5 هه اما اگه هر جا دیدین تو علامت دابل کوتیشن بود " " از نوع رشته ای میشه ینی الان اگه بود :




var = "5"


از نوع رشته ای بود
یه مثال دیگه اینکه الان اگه بگیم :



var = 5
Label.SetText("Label1", var);



تو خط دوم ، میگرده دنبال متغییر var ببینه تعریف شد یا نه اگه تعریف شده باشه ، تو شی لیبل ، مقدار متغییر var رو که 5 بود رو مینویسه. و چون قبل اش و تو خط بالاش ، متغییر var تعریف شد ، پس مقدارش رو مینویسه ینی تو شی لیبل نوشته میشه 5 (این خط دوم ، در واقع قضیه ی همون کسیه که گفت این جعبه هه چیه؟ Only the registered members can see the link) دستور Lable.SetText که یه چیزی رو تو شی لیبل ذخیره میکنه چ.م ما گفتیم var رو ذخیره کن ، گفتش بهمون که var چیه؟ (منظورش مقدار var بود دیگه) که ما تو خط بالاترش گفتیم var همون 5 هه پس 5 رو نوشت

حالا اگه من بنویسم :




var = 5
unknown = var
Label.SetText("Label1",unknown );



چی میشه؟ ینی بلد نیستین؟ Only the registered members can see the link هستینا
خوب گفتم سمت راست مساوی ها مقدار متغییره (سیب و .. Only the registered members can see the link) و سمت چپ مساوی ها هم خود متغییر ان (جعبه) که سمت راست (مقدار و محتوای متغییر که همون سیب بود) تو سمت چپ (که خود متغییر بود ینی همون جعبه) انداخته میشه و ذخیره میشه پس الان تو خط اول مقدار عدد 5 تو متغییر سمت چپ مساوی که اسمش var بود ، ذخیره میشه پس از این به بعد در جاهای دیگه ، در سمت راست مساوی (نه سمت چپ مساوی ها) و یا ورودی دستورها و تابع ها (که تو فیلم گفته شد)، اسم متغییر var برده شه یا به اصلاح همون فراخونی شه، مقدارش بجاش گزاشته میشه یا همون مقدارش برگردونده میشه که در اینجا مقدارش 5 بود
پس تو خط دوم که unknown = var نوشته شد ، چون var تو سمت راست مساوی هست پس نرم افزار میبینه قبلا متغییر var تعریف شد یا نه و اگه تعریف شده باشه ، مقدارشو توی سمت چپ مساوی که unknown بود ذخیره میکنه و اگه تو خط های بالاتر یا رویدادهای دیگه ای که زودتر از این رویداد اجرا بشه ، متغییر var تعریف نشده باشه ، نرم افزار ارور میده و تو اینجا هم چون مقدار متغییر var برابر 5 هه پس 5 داخل متغییر unknown ذخیره میشه . ینی هر جا از این به بعد اسم unknown برده شد ، مقدارش که 5 هه برگردونده میشه
و تو خط سوم هم که متغییر unknown اسم برده شد که چاپ بشه پس ، مقدارش که 5 هه چاپ میشه .
خوب اینو گفتم که دیگه قاتی نکنین چی تو چی ریخته و ذخیره میشه . ینی الان متغییر unknown تو خط دوم تو متغییر var ذخیره نمیشه . چرا شو که توضیح دارم میدم تو کتاب بالا Only the registered members can see the link پس تا این لحظه تو دستور بالا ، هر جا گفته شد var ، مقدارش که 5 هه برگردونده میشه و هر جا گفته شد unknown ، مقدارش که var که بازم مقدارش 5 هه برگردونده میشه
اما تو خط زیر قضیه فرق فوکوله Only the registered members can see the link




var = 5
var = "ahmad"
Label.SetText("Label1",var );



تو خط اول بالا ، اول مقدار 5 تو متغییر var ذخیره میشه و بعد تو خط دوم ، مقدار رشته ی ahmad تو متغییر var ذخیره میشه.ینی الان مقدار var کدومه به نظر شما؟ Only the registered members can see the link
آری درست گفتی (من که صدا تو نشنیدم . حالا بماند Only the registered members can see the link) آخرین مقدار var که خط دوم بود و مقدارش برابر رشته ی ahmad بود تو var ذخیره میشه . ینی تو خط های زیری هر جا گفته شد var ، آخرین مقدارش که تو خط دوم بود برگردونده میشه که ahmad بود ینی متغییرها قابلیت اور رایت شدن رو دارن. ینی متغییری که همنام قبلی باشه ، مقدار اولیه که تعریف شد براش پاک میشه و از این به بعد دیگه مقدار جدید جایگزینش میشه که تعریف شد
و خط سوم هم که گفت var رو بنویسه که همون آخرین مقدار var که ahmad بود رو مینویسه

یا مثلا اگه بگم :




var = 5
var = var+5
Label.SetText("Label1",var );



تو خط اول که 5 رو تو var میریزه .
تو خط دوم ، طرف راست مساویرو توی طرف چپ مساوی میریزه دیگه . ها؟ خوب طرف راست مساوی چیه؟ var+5 هه دیگه . خوب تو خط های بالا گفتم دیگه میگرده دنبال مقدار var (اونی که طرف راست مساوی هستا) . مقدار var تو خط اول پیدا میکنه که 5 بود بعد +5 میکنه که میشه؟ 10 و این 10 رو تو متغییر سمت چپ مساوی که باز var بود ذخیره میکنه ینی الان تو خط دوم ، مقدار متغییر var میشه 10 (ینی باز هم مثل کد بالا ترش که گفتم ، این متغییر var ، اوررایت شد)
از این به بعد هر جا اسم var برده شه (فراخونی شه) ، که تو خط سوم شد ، آخرین مقدار var رو که 10 بود رو ذخیره میکنه

اینو گفتم تا قشنگ سمت راست و چپ تون رو (نه سمت راست و چپ متغییر( رو تشخیص بدین هر چند تو فیلم هم گفته شد) ، برا اسب قضیه که الگوریتم تکثیرهOnly the registered members can see the link بزارین من برم ناهار بخورم برگردم ، ادامه شو بنویسم )Only the registered members can see the link


از این زیر تازه داستان تکثیر آرایه ، شرو میشه (اون بالایی رو گفتم واسه کسایی که قضیه ی متغییرها رو باهاش مشکل دارن و البته ضروریه واسه درک این الگوریتم زیر. هر چند الگوریتم معمولی ایه Only the registered members can see the link) :


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




o={10,20,30,40}




function clone(o)
new_o = {}
i,v = next(o,nil)
while i do
new_o[i] = v
i, v = next(o, i)
end
return new_o
end




ListBox.DeleteItem("ListBox1", -1);
NewArrayCount = Table.Count(clone(o));
for a=1,NewArrayCount do
ListBox.AddItem("ListBox1", clone(o)[a], "");
end



تو خط اول ، یه آرایه ای بنام o (او انگلیسی) تعریف شد که 4 تا عضو داره که میبینین. عضو اولش 10 و دومش 20 و سومش 30 و ... هه

خط دوم هم یه تابع هست که پارامتر ورودیش (داخل پرانتزش) ، همین آرایه ی o مون هست که اسم تابع ، clone هه

تو خط سوم ، یه آرایه ی دیگه که اسمش new_o هه تعریف شد (که قراره عضوهای اون آرایه ی o رو تو new_o ریخته شه)

تو خط چهارم که با دستور next آشنایی دارین (پست قبل) ، که



i,v = next(o,nil)


هست ، nil که میشه هیچ و پوچ (شما مثلا nil رو صفر در نظر بگیرین که ینی صفر امین عضو از آرایه) .پس این دستور میشه آرایه ی بعد ضفر امین عضو آرایه ی o (که میشه همون مقدار اولین عضو آرایه ی o که 10 هست) و این مقدار رو تو متغییر سمت راست ویرگول که v هست ، ذخیره میکنه و شماره ی اون آرایه (ینی اولین عضو آرایه ی o (دقت کنین نه اینکه مقدار اولین عضو رو ) بلکه شماره ی اولین عضو رو که میشه همون 1 ) رو داخل متغییر سمت چپ ویرگول که i هست ، ذخیره میکنه که اینا رو قبلا تو پست قبلی گفتم. پس الان مقدار متغییر i میشه 1 و مقدار متغییر v میشه 10

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

خط ششم که هستش



new_o[i] = v


، 6 طللیارد (درسته؟ Only the registered members can see the link) بار گفتم که مقدار سمت راست تو متغییر سمت چپ ذخیره میشه پس مقدار v داخل i امین عضو از آرایه ی new_o ذخیره میشه. مقدار v چی بود؟ 10 بود دیگه. ها؟ ینی همون مقدار اولین عضو آرایه ی اولی مون که o بود.
خوب i مون الان چنده؟ 1 بود دیگه آخرین بار؟ ها؟ خوب پس میشه 10 رو تو اولین عضو آرایه ی new_o مون ذخیره میکنه. تا اینجا اگه دقت کرده باشین ، مقدار اولین مقدار آرایه ی قبلی (o) که 10 بود ، فلا همون برابر اولین مقدار آرایه ی جدید (new_o) شد. پس فلا ینی اولین مقدارش کپی شد.

تو خط هفتم که



i, v = next(o, i)



هستش ، میگه چی؟ میگه عضو بعدی i رو تو آرایه ی o رو بریز تو متغییر v و شماره شو بریز تو متغییر i
خوب i مون تا حالا چی بود؟ 1 بود دیگه؟ ها؟ عضو بعدیش عضو 1 چی میشه؟ میشه عضو 2 . خوب مقدار دومین عضو آرایه ی o رو که 20 بود رو میریزه تو v و شماره شو که 2 بود رو میریزه تو i
پس مقدار v میشه 20 و مقدار i میشه 2
حالا چرا عضو بعدی رو گرفت؟ چون هم عضو بعدی آرایه ی قدیمی (o) رو گرفته باشه Only the registered members can see the link چون میخایم همه ی اعضاشو کپی کنیم دیگه و هم i هم یکی یکی جلو بره تا برسه به تعداد آرایه ی o که 4 تا بودن و بعد از اون ، بی مقدار بشه که شرط حلقه ی while (که مقدار دار Only the registered members can see the link بودن متغییر i بود) نقض شه تا از این حلقه خارج شه . ینی در واقع تو اینجا ، i همون شمارنده هست که به اندازه ی تعداد آرایه ی o که 4 تا ان مقدار خواهد داشت ینی 4 بار دارای مقدار میشه و بعد از اون ، بی مقدار میشه

تو خط بعدی میرسه به end شرط while که چون حلقه هه ، شرطش بررسی میشه و اگه شرطش درس بود ، تکرار میشه. شرطش هم که این هست که آیا متغییر i مقدار داره یا نه؟ داره یا نه؟ Only the registered members can see the link
آره دیگه آخرین بار مقدارش 2 شد دیگه . پس شرط برقراره و حلقه تکرار میشه

پس دوباره میرسیم به خط 6 و دوباره هم مقدار v رو داخل i امین عضو از آرایه ی new_o ذخیره میکنه. آخرین بار v بود 20 و i هم بود 2 پس 20 رو تو دومین عضو از آرایه ی new_o ذخیره میکنه
و این روند ادامه پیدا میکنه و حلقه تکرار میشه تا جایی که i برابر 4 بشه که همون برابر تعداد آرایه ی قدیمی مون که همون آرایه ی o بود ، بشه و در این زمان (ینی زمانی که i مون برابر 4 بشه) وقتی دستور خط هفتم ینی



i, v = next(o, i)


اجرا بشه ، چون مقدار عضو 4 ام آرایه ی o ینی پنجمین عضو آرایه ی o رو میخاد و چون آرایه ی o ، عضو پنجم نداره ، پس مقدارِ بی مقداری Only the registered members can see the link تو i ذخیره میشه ینی i دیگه مقدار نداره چون پنجمین عضو o وجود نداره و چون i بی مقدار میشه پس شرط while نقض میشه و دیگه تکرار نمیشه و خط بعدی ینی خط 9 اجرا میشه
پس تا وقتی که شرط while برقرار بود ، وقتی تا آخر (ینی تا زمانی که i مون برابر 4 میشد) جلو برین با همینی که توضیح دادم ، میبینین که اولین عضو آرایه ی o توی اولین عضو آرایه ی new_o ریخته میشه ، دومین عضوش هم تو دومین عضو ، سومین عضوش هم تو سومین عضو و چهارمین عضوش هم تو چهارمین عضو
ینی کلا کپی شد اون آرایه ی قبلی مون که o بود

خط بعدی هم که میشه خط 9 ینی



return new_o


که همونطور که تو پست قبل گفتم ، ینی new_o که آرایه ی کپی شده و جدید مون بود رو تو تابع مون که اسمش



clone(o)


بود ذخیره کن ینی هر جا اسم تابع



clone(o)


برده و فراخونی شد ، ینی مقدارش که همون آرایه ی new_o هستش ، برگردونده میشه

در ادامه ی کدها ، چون برای فراخونی مقدار و عضو متغییر ها باید شماره ی عضواش رو مشخص کنیم ، از حلقه ی for استفاده شد که از 1 تا تعداد تابع clon(o) که همون گفتم آرایه ی new_o ماست پس همون تعداد آرایه ی new_o ما میشه ، که در اینجا تعدادش 4 هست ، تکرار میشه ینی تو اینجا حلقه ی for ما هم 4 بار تکرار میشه تا هر 4 تا عضو آرایه ی new_o رو بگیره و تو لیست بوکس بنویسه پس همه ی عضوهای این متغییر که 10 و 20 و 30 و 40 ان ، تو شی لیست بوکس نوشته میشن (این تیکه رو ینی حلقه ی for رو زیاد توضیح ندادم چون به اندازه ی کافی تو فیلم بحث و تمرین شد)

فقط تو این حلقه درباره ی خط 14 ام که



ListBox.AddItem("ListBox1", clone(o)[a], "");


هست بگم که وقتی گفته شد



clone(o)[a]


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



clone(o)


برده شد ، ینی همون مقدار return ای که گفتیم ینی همون آرایه ی new_o . مثلا اگه a مون 1 باشه ، ینی اولین عضو از تابع



clone(o)


مون رو بنویس و این تابع هم چون گفتیم که همون آرایه ی new_o رو برگردون ، پس ینی در واقع ، گفتیم اولین عضو از آرایه ی new_o مون رو بنویس که کلا این تیکه گفتن نداشت چون یه لیست تو بالا توضیح داده شد Only the registered members can see the link


میدونم الان همه تون هجوم میارین تا این آموزشو ببینین ، فقط یکی یکی تو صف ایس کنین که حق همدیگه ضایه نشه Only the registered members can see the link
ان شاء ا... از هر 1000 نفرتون ، احیانا و خدای نکرده و زبونم لال Only the registered members can see the link یه نفر به این آموزش علاقه مند بود و خوند و تمرین کرد ، متوجه شده باشه (الان بخونه ، 2000 سال دیگه احتمال داره تموم کنه . Only the registered members can see the link)

SajjadKhati
05-08-14, 06:40
امنیت در AMS :

خوب اولا که امنیت رو بیشتر واسه فایلای دیتابیس به کار میبرن و دوما این پست قراره به امنیت فایلا و دیتابیس ای گفته شه که خودشون قابلیت رمز نگاری ندارن. مثل دیتابیس sql 3 (نه دیتابیس my sql که خود طرف همون موقع میتونه روش رمز بزاره و این قابلیت تو خود دیتابیس های my sql هست)

امنیت تو اینجا ،2 حالت و روش داره :

1) دسترسی فایل مورد نظر رو قطع کنیم که در این صورت تا دسترسی اش باز نشه ، نمیشه اطلاعات رو خوند یا روش نوشت و ذخیره کرد
2) با دستورات و توابع crypto ، روی فایل مورد نظر ، پسورد گزاشت که تو این روش ، روش ماهی آبی Only the registered members can see the link یا همون BlueFish گفته میشه. در این روش برای کد گزاری و رمز نگاری ، باید فایل مبدا رو بدین و فایل مقصد رو که حاوی همون فایل با رمز هست (البته با پسوند دیگه ای هم هست که واسه همین ، نمیتونین همون لحظه ازش استفاده کنین) و برای استفاده ، باید مسیر فایل مقصد و رمزش رو بدین تا کد گشایی شه و تو یه جای دیگه این فایلی که بدون رمز و قابل استفاده هه رو ، ذخیره و استفاده کنین


روش اول :

با دستور



File.SetPermissions("C:\\MyFile.ext", SID_EVERYBODY, DENY_ACCESS, ALL_PERMISSIONS, SUB_CONTAINERS_AND_OBJECTS_INHERIT);



تو ورودی یا همون پارامتر اول کد بالا ، مسیر همراه پسوند فایل رو بدین . حواستون باشه تو AMS هر درایو یا پوشه ای که بهش میرسین ، باید با دو تا (نه یکی) علامت \\ از هم جدا شن
میتونین دسترسی به یه فایل رو مسدود کنین که نباید در این صورت ، وقتی که از اتوران دارین خارج میشین ، فایل های مورد نیازی که برا اجرای اولیه ی بعدی لازمه مثل فایل autoran.exe و یا فایلایی که تو پوشه ی autoplay و ... هست رو مسدود کنین وگرنه اتوران بالا نمیاد و هیش کاریش نمیشه کرد. همونطور که گفتم ، بیشتر کاربرد ، تو امنیت و مسدود کردن فایلای دیتابیس هه. یادتون باشه که با این روش ، نمیتونین کاری کنین که طرف فایل رو از ویندوزش حذف نکنه ها. فقط با این روش ، تا حدود زیادی خیالتون راحت میشه که افراد عادی و... نمیتونن دیتابیس رو ویرایش و اجرا کنن (چون امنیت که 100 در 100 نیست Only the registered members can see the link)

برای استفاده از اون فایلی که مسدود کردین هم باید اول از مسدودیت خارجش کنین با تغییر تو ورودی سوم همون کد بالا ،اینجوری کد رو تنظیم کنین :



File.SetPermissions("C:\\MyFile.ext", SID_EVERYBODY, SET_ACCESS, ALL_PERMISSIONS, SUB_CONTAINERS_AND_OBJECTS_INHERIT);



خوب معلومه دیگه ممولا کد باز کردن فایل دیتابیس رو تو رویداد on startup مینویسن تا موقع ورود به اتوران ، قابل استفاده بشه و کد مسدود سازی شو موقع خروج اتوران تا کسی چش چپ به دیتابیس نگا نکنه Only the registered members can see the link



روش دوم :

استفاده از دستور همون ماهیه هست Only the registered members can see the link (دیگه موند پلنگ و یوز پلنگ شکار کنیم Only the registered members can see the link)
که برای پسورد گزاشتن از دستور



Crypto.BlowfishEncrypt("C:\\MyFolder\\MyFile.txt", "C:\\DestFolder\\MyFile.enc", "trustno1withthispassword");



استفاده میکنیم که تو ورودی اول ، مسیر همراه پسوند اون فایلی رو که میخایم رمز نگاری کنیم رو میدیم و تو ورودی دوم ، مسیر همین فایلی که رمز نگاری شدش ، حالا بگیم کجا ذخیره بشه رو (تو ورودی دوم ، پسوند فایل رو نباید عوض کرد و همش باید همین enc. باشه. فقط اسم و مسیر ذخیره شدن شو هر جایی که دوس داشتین ، بگین. نکته ی بعدی هم حواستون به مسیری که میدین باشه که گفتم باید بین هر درایو یا فایل یا پوشه ای که میزارین ، دو تا علامت \\ بزارین). تو ورودی سوم هم که پسورد رو تو علامت دابل کونیشن مینویسن . ینی الان پسورد این فایل تو اینجا میشه trustno1withthispassword (بی چاره اونی که بخاد این همه رو رمز گشایی کنه Only the registered members can see the link)

حالا برا باز کردن اون فایلی که پسورد داره و رمز نگاری شد (منظورم همون فایل مقصدی هه که مسیر ذخیره سازیش رو تو ورودی و پارامتر دوم کد بالا دادین که با پسوند enc. ذخیره شد) از دستور




Crypto.BlowfishDecrypt("C:\\MyFolder\\MyFile.enc", "C:\\DestFolder\\MyFile.txt", "trustno1withthispassword");



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



File.Delete("C:\\MyDir\\MyFile.ext", false, false, false, nil);


فایلی رو که کد گشایی کردین و ممولا دیتابیس هست (که تو اینجا اسمش مثلا MyFile.txt بود) رو پاک کنین تا بهش چپ چپ نگا نکنن Only the registered members can see the link

SajjadKhati
05-08-14, 06:56
نصب و استفاده و دانلود پلاگین های نرم افزار Autoplay Media Studio 8 (تو فیلم گفته نشد) :

اول درباره ی پلاگین ها تو AMS توضیح بدم یه کم ، بعد لینک دانلود شو براتون بزارم
کلا 3 (حالا یا 4 ، دقیقا نمیدونم) نوع پلاگین تو نرم افزار AMS داریم

اولی پلاگین اکشن (action plugin) هه که باعث میشه کدهای جدید به AMS اضافه شه و ملومه که اگه کد اضافه شه ، قابلیت نرم افزار بالاتر میره . پس یکی از مهم ترین پلاگین ها ، این نوع پلاگین ها هستن. این کدها ، به جمع کدهای دیگه میپیوندند (البته بعد از نصب و فعال کردن که میگم چجوری این کارو کنین) .
برای نصب این نوع پلاگین ها ، اول نرم افزار AMS رو ببندین و بعد آخرین پوشه ای که به اسم همون پلاگین هست رو تو اون جایی که نرم افزار رو نصب کردین ، داخل پوشه ای بنام Plugins>Actions ، کپی کنین . مثلا تو لینک زیر که میتونین دانلود کنین پلاگین رو ، اول پوشه ای بنام Plugin هست و بعد داخل پوشه ی action plugin ، پلاگین های مربوط به این قسمت هست . حالا اسم یکی از این پلاگین های اکشن ، هست tray (یه اکشن پلاگینی هه که کدها یا همون اکشن یا همون توابعی رو برای مدیریت بهتر آیکون تو system tray ایجاد میکنه مثلا میشه رو آیکون اش تو system tray ، کلیک راست کرد و یه گزینه هایی برا انتخاب بیاد). برای نصبش ، وقتی داخل خود پوشه ی tray برین ، یه فایل how to install و یه پوشه ی example و یه پوشه ی tray هست که منظور من این آخرین پوشه ی tray هست . (محتویات داخلش نه ها .) کل همین آخرین پوشه ی tray رو کپی کنین و داخل اون مسیری که نصب کردین AMS رو که بصورت پیش فرض ، C:\Program Files\AutoPlay Media Studio 8 Trial هست ، داخل اش تو پوشه ی Plugins و داخل اون ، تو پوشه ی Actions ، اون رو Paste کنین ینی بصورت پیش فرض ، پوشه ی tray ای که گفتم رو باید تو پوشه ی زیر ، کپی کنین :

C:\Program Files\AutoPlay Media Studio 8 Trial\Plugins\Actions

اگه دقت کرده باشین ، چون پلاگین ما از نوع action بود ، پس داخل پوشه ی action کپی شد (این تیکه همون بیشتر بدانید کتابا بود )
خوشال نشین زیاد . تا حالا فقط تونستین نصبش کنین. استفاده اش یه جور دیگه هه
برای استفاده از این اکشن پلاگین ها ، بعد از اجرای AMS ، از اون منوی بالا ، برین تو منوی Projects و گزینه ی یکی مونده آخری ینی Plugins
حالا لیست ای میاد که تو این لیست ، همه ی اکشن پلاگین هایی که نصب کردین ، تو این لیست میاد و هر کدوم رو که میخاین استفاده کنین ، تیک کنارشو بزنین . (یادتون باشه تو هر پروژه ی مختلف که باز میکنین ، باید این گزینه هایی رو که میخاین ، هر بار تیک شو اینجا بزنین)
بعد هم وقتی که میخاین برین تو قسمت action wizard ، ینی میخاین همون جایی برین که کدها رو بنویسین و انتخاب کنین ، میبینین که اَو ، اسم این پلاگین به همراه نوشته ی "Plugin" داخل پرانتز ، جلوش نوشته هه و حالا میتونین ازش استفاده کنین مثلا تو این مثال این جوری Tray (Plugin) نوشته میشه تو لیست اکشن ها




دومی هم پلاگین آبجکت یا شی (object plugin) هه که باعث میشه شی جدید اضافه بشه به AMS و به همراه اون ، کدها یا همون اکشن های مربوط به اون شی اضافه شه .
قضیه ی نصب این آبجکت پلاگین هم دقیقا مثل همون اکشن پلاگین هه با این تفاوت که اون پوشه ی مبدا (که درباره ی پوشه ی tray تو بالا توضیح داده بودم) رو بجای اینکه تو پوشه ی Actions کپی کنین ، تو پوشه ی Objects باید کپی کنین چون ملومه دیگه ، این از نوع آبجکن پلاگین هه . پس مسیر مقصدش بصورت پیش فرض میشه :

C:\Program Files\AutoPlay Media Studio 8 Trial\Plugins\Objects

برای استفاده از این پلاگین هم ، رو یه جای خالی تو صفه ، کلیک راست کنین و برین روی گزینه ی آخر ینی Plugins ، حالا تو اینجا ، لیست تمام آبجکت پلاگین ها رو میبینین که برای استفاده ی ازش ، باید انتخابش کنین و تو رویداد مخصوص خودش که داره و حتی میتونین از اکشن ها (کدها یا توابع) های مربوط به این پلاگین که تو قسمت action wizard میاد ، استفاده کنین



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

C:\Program Files\AutoPlay Media Studio 8 Trial\Plugins\Transitions

برای استفاده از این پلاگین ، باید روی صفه ی خالی پروژه ، کلیک راست و peroperties رو بزنین و از قسمت پایین صفه ای که باز شد ، ینی قسمت transition ، پلاگین مورد نظرتونو انتخاب کنین تا موقع ورود به صفه ، اون نوع انیمیشن اجرا بشه






حالا تو زیر ، براتون یه چن تا پلاگین آپلود کردم که بیشتراشون از مهم ترین پلاگین ها ینی action plugin ها هستن . میتونین از لینک زیر دانلود کنین :


Only the registered members can see the link (Only the registered members can see the link)




پلاگین هایی که توشه :


1) AMS Utilities Setup 2.6
2) Winapi V22
3) Download 11
4) ZipExBundle
5) RecycleBin
6) TaskBarListActionsPlugin
7) Tray 1
8) skin

9) ProgressEx




حالا توضیحات به ترتیب :


1) درباره ی اولی ینی AMS Utilities Setup 2.6 که قبلا توضیح داده بودم و مجموعه ای از هر 3 نوع پلاگین هه و کلا پلاگین ارزشمندیه . ینی تک دونه ی یکی یه دونه هه از اکشن پلاگین های این قسمت هم میشه به HTTP که برای ادامه ی دانلود های قطع شده یا خراب شده هست و ... . اکشن پلاگین FTPS برای آپلود فایل و ... هه . اکشن پلاگین Math ، کدها و توابع مهم برای ریاضی هه مثلا برای محاسبه ی Nan و Nor و ... (علاوه بر توابعی که اکشن Math داره) و ... و همینطور انواع transition plugin مثل Metro و FadeIn و ...
این پلاگین ، بصورت ستاپ نصب کردنی هه . ینی کپی کردنی نیست . اما قضیه ی استفاده رو ، باید از طریقش همونطور که قبلا گفتم برین

2) یکی دیگه از تکدانه (آب میوه نه ها ) ی دوردونه که بالای 100 تا اکشن پلاگین داره که مخلوطی از اکشن ها و کدهای مختلف ان
مثلا مهم ترین هاش میشه به اکشن هایی مثل کم و زیاد کردن وضوح (transparency) پنجره ی اتوران و حتی بقیه ی پنجره ها (WinApi.SetWindowTrans) و برای عکس گرفتن از پنجره ها (مثلا پنجره ی اتوران) WinApi.CaptureWindow و برای نشون دادن یا ندادن و یا مینیمایز کردن کل پنجره ها (مخصوص وقتی که میخاین صفه دسکتاپ فقط نمایش داده شه) و ایجاد انواع شکل های هندسی مثل بیضی و ... و مدیریت Rycycle bin و فعال و غیر فعال کردن موس و کیبرد برای بضی از پنجره ها و ایجاد نوار وضعیت (WinApi.CreateWindowEx) و خیلی از اکشن های دیگه

3) سومی هم اکشن پلاگینی برای دانلود همزمان چندین فایل با قابلیت مکث و ادامه هه (نمونه ی این اکشن هم تو پلاگین اولی هست اما از این اکشن استفاده کنین بهتره. ینی اون اصلا کار نمیکنه resume اش انگار اما این یکی کار میکنه ولی گاهی اوقات ممکنه ارور بده. پیشنهادم اینه که اول با کد و اکشن پیش فرضی که تو قسمت HTTP نرم افزار AMS هست (نه پلاگین HTTP اولی که گزاشتما) ، برای دانلود قدم پیش بزارین ، بعد اگه نصفه نیمه کاره طرف ول کرد ، با این پلاگین ، ادامه شو انجام بدین .)
نکته ی مهمی که درباره ی این پلاگین باید بگم اینه که در هر صورتی که طرف بخاد کنسل کنه یا اینکه وسط دانلود و یا حتی بعد از اینکه دانلود تموم شد ، از نرم افزار و اتوران خارج شه ، قبل از کد خروج ، حتما کد استوپ دانلود و حذف دانلود سیژن که هر دو کار را کد



result = Download.Delete(DownloadSesion);


انجام میده رو بزارین و بعد از این کد ، کد دلخواه خودتونو مثلا خروج از نرم افزار رو بزار وگرنه برنامه تون کرش میشه
دقت کنین که متغییر DownloadSesion تو کد بالا ، متغییر عددیه که برا کد Download.Start هست ینی کد یا تابع یا همون اکشن Download.Start ، عددی رو برمیگردونه اینجا که تو متغییرذخیره DownloadSesion ذخیره کردیم و این عدد رو برای استوپ دانلود و حذف دانلود سیژن ای که گفته بودم ، باید به کد Download.Delete داد (همونطور که تو بالا میبینین)

4) اکشن پلاگینیه برای اکسترکت و ساخت فایل های Rar (بدیش اینه که رایگان نیست و 14 روزه هه . اگه کسی سریال اینو پیدا کنه که خیلی ممنون میشم یا نسخه ی رایگانشو پیدا کنه)

5) اکشن پلاگین برای مدیریت Rycycle Bin

بقیه هم که چندان نیاز به توضیح نداره و از نوع اکشن پلاگین اند و توضیح آبجکت پلاگین ProgressEx رو قبلا داده بودم تو پست صفه قبل




راستی میتونین از این سایت ، پلاگین های بیشتری رو دانلود کنین (Only the registered members can see the link)

SajjadKhati
05-08-14, 07:01
نکته ی دیگه :

اگه یه متغییری تعریف کرده باشین که هنوز اجرا نشده باشه یا کلا اصلا یه متغییر رو تعریف نکرده باشین ، مقدار nil رو برمیگردونه اون متغییر
مثلا اگه تو رویداد کلیک راست شی ای یه متغییر عددی ای بنام Count=5 تعریف کرده باشین و اگه رو اون شی هنوز کلیک راست نکرده باشین ، پس این متغییر تعریف نشد و اگه در این موقه تو رویداد کلیک چپ این شی ، این متغییر رو بخاین فراخونی کنین ، nil رو برمیگردونه که باید این جوری براش بنویسین :



if Count~=nil then
اینجا دستورات رو بنویسین
end


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

در کل منظورم اینه که nil فقط برای متغییرها یا بهتر بگم تابع ها و اکشن هایی که بصورت پیش فرض تو خود برنامه ی AMS هستن و موقه رخ دادن خطا اتفاق میافتن ، نیست بلکه واسه همه ی متغییر های تعریف نشده و یا همه ی متغییر های اجرا نشده هم هست

nil هم که میدونین به منی هیچ و پوچ هست (ینی هیچ چی متغییر تعریف نشد و یا این متغییر یا تابع یا آرایه ، پوچ هست و خطا اتفاق افتاد)

SajjadKhati
05-08-14, 07:06
یه نکته ی دیگه درباره ی تغییر (کد نویسی برای تغییر) رنگ :
اینه که چون رنگ ها هر کدوم شون یه عدد (کد) ای دارن پس متغییرشون از نوع عددی هه اما یه مشکل کوچیک اینه که از نوع اعداد شونزده دهی (هگزا دسیمال) ان که برای استفاده اش باید اول اون عدد رو که حتی میتونین از عدد رنگ تو همین انجمن (نوشته ی زیر رنگی که انتخاب میکنین و با علامت # شروع میشه) یا تو همون AMS این عدد ها رو به عدد دهدهی تبدیل کنین ، و بعد استفاده کنین وگرنه جواب نمیده

مثلا عدد هگزا دسیمال رنگ سفید ffffff# هست که باید اول به عدد دهدهی تبدیل اش کنین که میشه 16777215
بدشم که خودتون اوستایین و با کد و آرایه ی مخصوص خودش که تو فیلم کاملا توضیح داده شد ، اجرا و تغییر رنگ میدین . ینی مثلا اگه بخین برا یه شی Label1 برا حالت نرمال اش تعریف کنین :



Label.SetProperties("Label1", {ColorNormal=16777215});


یا مثلا عدد رنگ سیاه هست 000000# که تو عدد دهدهی اش هم میشه 0

SajjadKhati
05-08-14, 07:09
تبدیل عدد هگرادسیمال (شانزده دهی) به دسیمال (دهدهی) :

من هی عذاب وجدان میگیریما. بزارین اون تبدیل عدد شونزه دهی رم بگم
کلاس اول ابتدایی ، یکان ، دهگان ، صدگان خوندینو بلدین هَندا (هنوز)؟ این هم عدل همونه
مثلا 251 رو به یکان و دهگان و صدگان بخایم تبدیل کنیم ، چی میشه؟
میشه یکی ، یکان (یکی ، یک تایی) و 5 تا دهگان و 2 تا صدگان که 2 تا بسته ی صدتایی که میشه 200 تا رو با 5 تا بسته ی دهگان که میشه 50 تا رو با یکی یکان جمع میکردیم و میشد 251
ینی شد :

(1*1)+(10*5)+(100*2)

تا اینجا حالی بَیِّه؟ (ینی متوجه شدین؟) خا بریم ادامه
خا حالا اینو بصورت ریاضی بگین . خوب 2 و 5 و 1 که تو معادله ی بالا سرجاشون هست ان . با اینا که کاری نداریم . منظور من 100 و 10 و 1 معادله ی بالاست. خوب معلومه که عددهای ممولی (نقی نیستا ) ما در مبنای 10 ان پس این 3 تا عدد 100 و 10 و 1 رو یه جوری باید بگین که وقتی عدد 10 رو صربدر خودش میکنیم ، این اعداد بدست بیاد .
ینی 10 ضربدر چه عددی میشه 100؟ ملومه دیگه 10
ینی 100=10*10
ینی 10 به توان 2 میشه 100 پس جوابش میشه 10 به توان 2
حالا 10 را ضربدر چه عددی کنیم ، میشه 10؟ منظورم اینه که توان 10 چند باشه که خودش بشه؟ ملومه دیگه 1
بدش 10 به توان چند میشه 1؟ بازم ملومه ، 0
پس میشه :

(1*10 به توان 0)+(5*10 به توان 1)+(2*10 به توان 2)

خوب ، تا اینجا رم که متوجه شدین؟ (حالا نمیدونم اینجا چجوری توان رو بالای عدد مثل تو ریاضی بنویسم ، این جوری بصورت فارسی نوشتما )
حالا تو محاسبات ، 4 ساعت نمیتونین جذر 100 رو بگیرین که یادتون بیاد میشه 10 به توان 2 که میخاین جین فنگی حساب کنین ، چی کار میکنین؟
میدونین دیگه ، اصل اون توان هه هست که مهمه و اگه بدونین به توان چن میشه ، دو سوته تو ماشین حساب میزنین و بدیت میارین
10 که مبنای ما بود و باهاش کاری نداریم . هیچ چی . توانو چجوری سریع بدست بیاریم؟
عدد ما بود 251 درسته؟
همیشه توان مبنای 10 ، اولین عدد از سمت چپ (در اینجا عدد 1) میشه 0 و توان دومین عدد از سمت چپ (در اینجا عدد 5) میشه 2 و توان سومینعدد از سمت چپ (در اینجا عدد 2) میشه 2 و ... ینی توان n امین عدد از سمت چپ ، میشه n-1
ینی اینکه الان 251 ، 3 تا دونه عدد نیست؟
عدد 2 (صدگان) ، چندمین عدد از سمت چپ هه؟ 3 امین عدد دیگه ، پس یکی کم کنین ، میشه 2 خوب؟ پس توان عدد 2 (صدگان) عدد 251 میشه 10 به توان 2
عدد 5 (دهگان) ، چندمین عدده؟ دومین دیگه؟ ها؟ یکی کم کنین میشه 1 . پس توان عدد 5 (دهگان) ما میشه 10 به توان 1
آخری هم که توان 10 اش یکی کم کنین میشه 0
پس بازم رسیدیم به همون بالایی (ضرب و جمع رو گفتم دیگه مثل بالایی هه)

خسته کیه؟
تکبیر

اسب نرم افزار Matlab :
حالا این مفعوم اعداد دهدهی بود . بریم سوراخ (چی سراغ) شونصد دهی
اول بزارین اینو بگم . همنطور که میدونین که نمیدونین ولی میدونین چرا اعداد تو کامپیوتر 0 و 1 ان؟ ینی به اعداد 0 و 1 چی میگن؟ میگن دودویی یا همون دو بیتی (غزل نیستا. بِیت نخونین ) . نه اینکه دو تا عدد باشه بهش بگن دو بیتی ها . مثلا دهدهی به چه اعدادی میگن؟ از عدد 0 تا 9 درسته؟ پس مثلا هر وقت شنیدین دهدهی ینی عدد هه از 0 شرو میشه تا یکی کمتر از 10 که میشه 9
یا مثلا تو همین دودویی ینی از 0 تا یکی کمتر از 2 که همون میشه 1
یا شونزده دهی هم ینی از 0 تا یکی کمتر از 16 ینی 15
قضیه ی اعداد هم همه ایجوریه
اما ما عدد بزرگتر از 9 نداریم (منظورم تو اعداد و تنوع اعداد هست که از 9 بزرگتر نداریم و بقیه مخلوطی از 0 تا 9 ان) . مثلا ما که میگیم 10 ، ده نیست . مخلوطی از 1 و 0 هه
برا اعداد شونزده دهی هم همینجوره چون بزرگتر از 9 وجود نداره ، مجبور شدن ، بجای عدد 10 تا 15 ، حروف الفبا را به ترتیب بزارن تا به 15 برسه (گفتم دیگه عدد شونزده دهی ینی از 0 تا 15)
اونا این حروف ها ان و هر جا حروف شون رو دیدین ، عدد بجاش بزارین :

10 --> a
11 --> b
12 --> c
13 --> d
14 --> e
15 --> f

خوب حالا باید اتوماتیک طبق چیزایی که تو بالا گفته شد ، تبدیل رو بلد باشین دیگه؟ ها؟

مثلا عدد شونزده دهی 00FD2C رو میخایم به دهدهی تبدیل کنیم :
چون شونزده دهی هست عدد مبدا ما (عددی که میخایم تبدیل کنیم ، ) پس 16 رو مینویسیم ینی پایه ی ما 16 هه و توانش مهمه
توانش هم اولین عدد از سمت چپ که C باشه 0 میشه. خود C هم که میشد عدد 12 پس تا اینجا میشه 12*16 به توان 0
توان دومین عدد از سمت چپ که 2 باشه ، میشه 1 که میشه 2*16 به توان 1
توان سومین عدد از سمت چپ که D باشه میشه 2 . خود D هم که میشه 13 پس میشه 13*16 به توان 2
توان چهارمین عدد از سمت چپ که F باشه میشه 3 و خود F هم که میشه 15 پس میشه 15*16 به توان 3
اون صفرهای سمت چپ هم مثل عددهای ممولی (مثلا 00100 که میشه همون 100) هم که حساب نمیشه
پس کلا میشن :

(12*16 به توان صفر)+(2*16 به توان 1)+(13*16 به توان 2)+(15 *16 به توان 3)

که با یه حساب سر انگشتی (ماشین حسابی ) میشه :

64812 = 61440+3328+32+12

باز چیز دیگه ای هست که میخاین یاد بگیرین ، بگین ، تارف نکنینا

SajjadKhati
05-08-14, 07:15
یه نکته ی کوچولوی دیگه درباره ی رنگ ها :

خسته که نشدین؟ ها ؟
الان زنگ تفری رو میزنم 2 دیقه بصبرین
اینو که الان دونستین که رنگ ها بصورت عدد شماره گزاری میشن و چجوری تبدیل اش کنین؟ و از 0 شرو میشن تا 16777215
حالا اینکه یه قضیه ی دیگه اینکه هر رنگ (منظورم پر رنگ اون رنگه ها ) ینی مثلا اگه آبی منظورتونه ، آبی پر رنگ رو میگم ، تا بخاد کم رنگ بشه ، 255 بار تغییر میکنه
مثلا یکی از اعداد آبی پر رنگ 16711935 هه . از این عدد تا 255 تا اگه بالاتر برین ، میبینین که آروم آروم رنگ آبی پر رنگ داره به بنفش تبدیل میشه ینی از عدد 16711935 که واسه آبی پر رنگ هست تا 16712191 که بنفش هست (که اختلافشون 255 تاست) ، آروم آروم آبی پر رنگ به بنفش تبدیل میشه اما وقتی همین عدد یه دونه بالاتر بره و بشه 16712192 ، یهو رنگ یه دفه عوض میشه

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


اینو تو رویداد On Show بزارین :



CT = 16711935
Paragraph.SetProperties("Paragraph1", {ColorNormal= CT});
Page.StartTimer(100, 1);


اینو تو رویداد On Timer :



if e_ID == 1 then
CT = CT + 1
Paragraph.SetProperties("Paragraph1", {ColorNormal= CT});
Label.SetText("Label1", CT);
end




یه شی Label1 و Paragraph1 هم بزارین تو همون صفه

SajjadKhati
05-08-14, 07:20
بازم یه نکته ی دیگه :

جدنی خسته شدین؟
تو رو خدا؟
پس اندکی بتحملین
تنا کلاسیه که شاگرد نداره
شوخی بسه ، جدی دارم میگما . خوب گوش کنین وقت کلاس گرفته شد

خوب اساتیدای بالام جان ، این AMS هم گاهی هول برش میداره ، اندکی قات میزنه موقه تغییر رنگ حتی اگه تبدیل اعداد هگزا دسیمال رو به دسیمال هم درست انجام بدین (البته گاهی و بهتر بگم بیشتر اوقات)
برا اینکه عدد رنگ رو درست بدست بیارین تو AMS اول یه رنگ (مثلا رنگ آبی پر رنگ) رو تو یه شی (تو اینجا مثلا بنام Paragraph1) تنظیم کنین و از تابع زیر استفاده کنین (که البته آموزش نحوه ی استفاده ی کامل این تابع ها و اینکه آرایه ای رو که برمیگردونه چجوریه استفاده اش تو فیلم گفته شد) :



ColorText = Paragraph.GetProperties("Paragraph1");
Dialog.Message("Notice", ColorText.ColorNormal, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);

SajjadKhati
05-08-14, 07:26
درست کردن (کد نویسی) دیالوگ دستی با پنجره ی DialogEX :

خوب اساتیدا ، از اونجایی که شما فیلمو دیدین ینی ندیدین ، و جواب سئوال بالای منو هم دادین ینی ندادین و منم خیلی ازتون تشکر میکنم ینی بازم میکنم پس بزارین این قضیه هم بگم
تو فیلم دیدین دیگه ، تو AMS هر جا نامی از دیالوگ برده میشه ینی یه صفه ای که حالا یا پیام داره فقط یا با گزینه هایی که انتخاب میکنین ، میتونین کار خاصی انجام بده مثلا گزینه ی yes یا no داشته باشه یه دیالوگ
تا اینجا دید یو آندرستود؟
خا ، حالا دو نوع دیالوگ داریم تو AMS . یکی پنجره ای هستش که فقط اسمش Dialog هه (البته بازم من منظورم بیشتر کد Dialog.Message هه) که این نوع پنجره ، از پیش تعریف شده هه و چن گزینه ی قابل تغییر بیشتر نداره (Abort و Cancell و Yes و No و ...) اما نپجره ی دیگه ای هست بنام DialogEX هه که میتونین مثل صفه ی اتوران ، حتی گرافیکی کار کنین اینترفیس اش رو و هر جور که خودتون خاستین
اینا رو ول کنین ، بریم سر خاستگاری چی همون اسب Matlab (این قد ممدرضا گفت که ما رو به بی راهه کشوند )
مهم کد نویسی شونه و اینکه چه متغییر که اینجا عدده رو برگردونه
تو کد نویسی Dialog.Message که تو فیلم توضیح داده شد و اینکه دکمه ی Yes چه عددی رو برمیگردونه وبقیه ی کد ها ...
تو کد نویسی برای صفه ی DialogEX ، مثلا 3 تا شی (تصویر یا آیکون یا حتی نوشته) میزارین که اگه طرف آیکون اولی رو انتخاب کرد ، مثل مثال Dialog.Message ، یه عدد رو برگردونه و اگه آیکون دومی رو انتخابید ، یه عدد دیگه و ...

یادتون باشه اولین و تنها استثنایی که تو برگردوندن یا همون ذخیره کردن متغییر ها هست ، تو همین قضیه ی DialogEX هه . این جوری که وقتی تو صفحه ی اصلی پروژه (صفه ی DialogEX منظورم نیست) ، با کد DialogEX.Show ، اون صفه ی دیالوگ (منظورم DialogEX هه) رو نمایش بدین ، وقتی موقه بستن همین صفه ، با کد DialogEX.Close ، تو اون پارامتر یا همون ورودی اول همین کد ، هر عددی رو که بنویسین (انتخاب این عدد ، دلخواهی هه) ، این محتوای متغییر (که از نوع عدد) هست ، توی اون متغییری که باهاش این صفه ی DialogEX نمایش داده شد ، ذخیره میشه
من که میدونم متوجه نشدین
بزارین مثال بزنم :
مثلا تو پروژه ی اصلی تو یه رویدادی ، آیکونی ، چیزی مینویسین :



Message = DialogEx.Show("Dialog1", true, nil, nil);


اینو که میدونین ، باعث میشه که صفه ی DialogEX که طراحی کرده بودیم خودمون رو نمایش بده و متغییر اش هم که اسم شو گرفتیم Message
هر عددی هم موقه خروج تو کد DialogEX.Close که بعدا مینویسیم ، تو این متغییر اش ینی متغییر Message ذخیره میشه
3 تا گزینه یا آیکون تو این صفه ی دیالوگ طراحی کردیم که هر کدوم شون با کد DialogEX.Close ، اون کدی رو که تو پارامتر اول این کد مینویسیم ، برمیگردونه تو متغییر Message
پس باید تو هر 3 تا آیکون کد DialogEX.Close رو بنویسیم
چون عدد پارامترش دلخواهی هه ، اولی رو 1 و دومی رو 2 و سومی رو 3 میگیریم
پس تو آیکون اول مینویسیم :



Close = DialogEx.Close(1);


عدد 1 پارامتر اول این کد رو که توضیح دادم
الان این عدد 1 رو اگه کسی با AMS آشنایی داشته باشه یه خورده ، انتظار داره که تو متغییر خود همین کد DialogEx.Close که اسم این متغییر رو Close گرفتیم ، ذخیره شه
اما گفتم که استثنا داره
این عدد 1 ، تو اون متغییری ذخیره میشه که باعث باز شدن این صفه ی دیالوگ شده بود ینی تو متغییر کد DialogEx.Show که تو اینجا اسمشو Message گرفته بودیم
پس از این به بعد با انتخاب اولین آیکون این صفه ی دیالوگ ، این صفه بسته میشه و عدد 1 (عددی که تو اولین پارامتر کد DialogEx.Close) نوشته بودیم ، تو متغییر Message (متغییر اون کدی که باعث باز شدن اون صفه ی دیالوگ شد) ذخیره میشه
حالا همین کار را برا آیکون دوم صفه ی دیالوگ میکنیم . که قبلا توضیح داده بودم ینی مینویسم :



Close = DialogEx.Close(2);


و همینطور واسه آیکون سوم

حالا برا کد نویسی اصلیش که دیگه اوستایین و از این مثالای پیش دست و پا شکسته و افتاده رو 6000 بار کار کردیم دیگه؟ ها؟
تو ادامه ی کد همونجایی که کد باز شدن صفه ی دیالوگ (DialogEx.Show) رو نوشته بودین ، میگین که اگه این متغییر Message ، 1 بود (ینی اگه گزینه یا آیکون اول تو صفه ی دیالوگ کلیک یا انتخاب شد) ، فلان کار رو کنه و اگه برابر 2 بود ، فلان کار رو کنه و ... ینی تو ادامه :



if Message~=-1 then
if Message==1 then
Dastorat ro inja benevisin
elseif Message==2 then
Dastorat ro inja benevisin
elseif Message==3 then
Dastorat ro inja benevisin
end
end



تو خط اول گفته شد مخالف -1 هر وقت بود این کد ها اجرا شن چون اگه موقه بستن دیالوگ (کد DialogEX.Close) ، اروری اتفاق بیفته ، تو متغییر کد نمایش دیالوگ که تو اینجا Message بود ، -1 (منفی یک) برگردونده میشه
از اونجایی که شما مثل من فراموش کارین ، اگه صفه ی DialogEX ، بصورت استانداردباشه و کاربر قابلیت اینو داشته باشه که از بالا ، گزینه ی ضربدر رو انتخاب کنه و از این طریق ببنده ، در این صورت ، تو همون متغییر صفه ای که دیالوگ رو نمایش میده (در اینجا متغییر Message) عدد مربوط به دکمه ی کنسل که همیشه یا اغلب اوقات عدد 2 هست ، برگردونده میشه پس یادتون باشه که اگه صفه ی دیالوگ بصورت استاندارد هست ، موقه انتخاب عد تو پارامتر اول کد DialogEx.Close ، عدد 2 رو انتخاب نکنین که با این کد کنسل قاتی شه و ندونین قضیه از کجاهه (عدد منفی هم یه وقت نزارین . حالا گفتیم که دلخاهی هه )
دید یو آندرستند بالام جان های گل مهندسین؟

SajjadKhati
05-08-14, 07:31
حذف آرایه :
اساتیدها ، یادتون باشه که اگه یه آرایه ای از قبل اعضاش مشخص شده باشه ، و بدش بیاین همون آرایه رو فقط تعریف کنین (اعضاش رو نه ها) ، کل اعضای اون آرایه پاک میشن و برابر nil میشن
مثلا اگه از قبل این آرایه رو با اعضاش توی رویداد On Preload یه صفه تعریف کرده باشم :




Array = {}
Array[1] = 53
Array[2] = 48
Array[3] = 9




خوب این که ملومه مثلا تو رویداد On Click یه شی بنویسم :




Dialog.Message("Notice", Array[1], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);




مقدار اولین عضوش که 53 بود رو چاپ میکنه
و اگه حالا تو رویداد On Leave اون شی فقط این آرایه رو تعریف کنم (اعضاش رو نه ها) ینی فقط تو این رویداد بنویسم :




Array = {}



حالا تاوم عضوهای آرایه پاک میشن و یه آرایه ی خالی جایگزین میشه و حالا اگه دوباره روش کلیک کنین ، یه پیام میده که اولین عضوش خالی هه (ملومه که اگه تو رویداد On Leave ، عضوهاشو مشخص کنیم ، این عضوهای جدید تعریف میشن دیگه و عضوهای قبلی همونطور که گفته شد ، هر چن تایی که بودن ، حذف میشن)

SajjadKhati
05-08-14, 07:36
نحوه ی کد و الگوریتم جدید دادن به یک رویداد (رویداد اشیاء) :
ینی چی؟ ینی اینکه مثلا شی ای وجود نداشته باشه و شما اون شی رو بوجود بیارین و بعد برا رویدادهای مختلف اون شی (مثلا کلیک چپ و ...) اش کد بنویسین و یا حتی برا پاک کردن و تعویض کد نوشته شده داخل هر رویداد یا شی دیگه ای کد بنویسین که البته این کد نوشتن ، تو هر رویداد و یا اشیاء دیگه ی داخل اون صفه میتونه اتفاق بیفته و بنویسین .
اول موارد ابتدایی شو بگم که همه تون بلدین




Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});



این کد برای ایجاد یه شی تو صفه هست




Page.SetObjectScript("NewObject", "On Click", "");



این کد بالا هم برای قرار دادن کد و الگوریتم جدید برا شی جدید هست



و اما اصل مطلب که نحوه ی قرار دادن و نوشتن الگوریتم جدید برا یه شی و یا رویداد جدید هست :

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




Application.SetPageScript("Page1", "On Show", "");




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




ABC = "Dialog.Message(\"Notice\", \"Salam\", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);





اول خط 2 و 3 رو میگم و بعد خط اول . تو خط دوم ، تو پارامتر اول اش ، نوع شی ای که میخاین ایجاد کنین رو مشخص میکنین (مثلا شی تون از نوع دکمه یا Button هست یا از نوع تصویر یا Image و یا هر چیز دیگه ای ...) و تو پارامتر دوم ، اسم شی ای که میخاین ایجاد کنین رو میدین و تو خط سوم هم که خصوصیاتش رو (مثلا شکل شی تون از کجا لود شه و اندازه و X و Y و فونت و ... اش چی باشه که تو فیلم هم توضیح داده شد و تو راهنماشم هست)
تو خط سوم ، پارامتر اولش ، باید اسم اون شی ای رو که میخایم کد جدید بهش بدیم ، رو مینویسیم (چون میخایم به شی ای که جدید ساختیم کد اضافه کنیم و اسمشم NewObject گزاشته بودیم تو تابع قبل ، پس اینجا هم همون اسمشو میزاریم) و تو پارامتر دوم اش ، اسم دقیق رویداد اون شی رو مینویسین . مثل خود رویداد ای که تو شی ها نوشته هه مثلا تو اغلب شی ها برا کلیک چپ اسم رویدادش نوشته هست "On Click" که حرف اول شون بزرگ هست و بین هر دو کلمه اش فاصله داره . شما هم باید دقیق همینجوری بنویسین بدون حتی یه کاراکتر کم یا زیاد اونم با توجه به حروف بزرگ و کوچیک وگرنه ....Only the registered members can see the link یا مثلا برا کلیک راست باید بنویسین "On Right-Click" و ... که اسم همه ی رویداد ها نوشته هه تو شی خودش و میتونین از اونجا تقلید کنین . تو پارامتر یا همون آرگومان سوم که مهم ترین و ساده ترین بحث هست ، باید کد و یا اسم متغییری رو که شامل کد و توابع اون هم فقط بصورت رشته هست رو بدین که تو این پارامتر ، متغییر ABC که تو خط اول تعریف شد ، نوشته شد


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




ABC = " Dialog.Message("Notice", "Salam", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); "



الان این کد خط بالا کامل نیست ها . کامل اون بالایی هست . این اول بسم ا... اش هه Only the registered members can see the link . بد تو هر پارامتر همین خط ، هر جا رشته بصورت مستقیم بود (نه اینکه متغییر ای باشه که رشته رو برگردونه . فقط رشته ی مستقیم داخل اش بکار رفته باشه مثل پارامتر اول و دوم خط بالا) قبل علامت " اش یه علامت \ میزارین ینی پارامتر اول که "Notice" هست را باید این جوری بنویسین "\Notice"\ و همینطور پارامتر رو دوم رو باید بنویسین "\Salam"\ پس میشه :




ABC = "Dialog.Message(\"Notice\", \"Salam\", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"



دومین نکته اینکه اگه تو همین متغییر ABC خط بالا ، بخاین دو تابع یا کد بنویسین ، نمیتونین با زدن اینتر ، بعدی رو تو خط پایین تر اش بنویسین . باید با علامت ; هر خط رو از هم جدا کنین

مثال دیگه :




ABC = "Text1 = \"Salam Khobi?\";Text2 = \"Koja Bodi?\";Dialog.Message(\"Notice\", Text1..Text2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);




خوب Salam Khobi? چون رشته هست ، پس باید قبل " یه دونه \ بزاریم و همینطور برا Koja Bodi? که میشه اونی که تو بالا و خط اول دیدین و چون 3 تا دستور جدا ینی Text1 و Text2 و تابع Dialog.Message داریم ، پس بین همه شون باید نشونه ی جدا کننده ینی ; بزاریم اما تو پارامتر دوم تابع Dialog.Message درسته که متغییرها از نوع رشته هستن اما چون رشته بصورت مستقیم نیومده و متغییر اومده فقط ، پس علامت "\ نمیخاد و مثل حالت عادی تعریف میکنیم ینی Text1..Text2 که در ادامه ی همدیگه نوشته میشن که میدونین

سومین و مهم ترین نکته اینکه تو این دستور جدیدی که خودتون دارین مینویسین (در اینجا همون متغییر ABC منظورمه) ، اگه رشته تون به هر نحوی داخلش علامت \ داشت ، نمیتونین داخل این متغییر ABC تعریف کنین و باید خارج از این متغییر ABC تعریف کنین و داخل دستوراتش اون متغییر رو فراخونی کنین مثلا اگه بخاین تو همون دستور خط اول بالا بنویسین :




ABC = "Text1 = \"Salam Khobi?\n\";Text2 = \"Koja Bodi?\";Dialog.Message(\"Notice\", Text1..Text2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"



چون بعد از Salam Khobi? علامت n\ اومد و چون علامت \ داره همراه خودش ، موقه اجرا ارور میده . برا این کار ینی هر رشته ای که بین اش علامت \ داره (بجز علامتی که گفته شد برا خود رشته تو اول و آخرش میزارین) ، باید قبل این متغییر که در اینجا ABC هه تعریف و تو توابع این متغییر ABC فراخونی کرد ینی نوشت :




Text1 = "Salam Khobi?\n\n"
Text2 = "Koja Bodi?"
ABC = "Dialog.Message(\"Notice\", Text1..Text2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);




چون متغییر Text1 و Text2 بیرون از این رشته ی متغییر ABC تعریف شد پس بصورت عادی باید نوشته شه ینی لازم نیست قبل علامت " علامت \ گزاشت و چون تو ته خط اول اش n\n\ گزاشته شد ، پس ملومه که بین شون به اندازه ی 2 خط فاصله میافته

تذکر : این نکته ی سوم که درباره ی علامت \ بود ، برا مسیر فایل یا فولدر هم صدق میکنه چون بین هر درایو یا پوشه ، این علامت رو که جدا کننده هست داره پس برا این نوع دستورات هم باید قبل از رشته ی دستورات جدید (در اینجا ABC) تعریف کرد و داخل توابع این رشته (در اینجا ABC) ، فراخونی کرد مثل مثال بالا و یا پایین :




Path = "narm afzar\\Anti viruse\\Avast Internet Security 8.0.1497.376 Final\\Avast Internet Security.jpg"
ABC = "File.Open(Path, \"\", SW_SHOWNORMAL);"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);




تو پارامتر دوم خط دوم هم که چون رشته ی خالی هست و قبل هر علامت " باید علامت \ گزاشته شه ، پس میشه همونی که تو بالا دیدین ینی "\"\

این هم مثال آخر از چند دستوری که اول فایل مورد نظر اجرا و بعد پیام میده :




Path = "narm afzar\\Anti viruse\\Avast Internet Security 8.0.1497.376 Final\\Avast Internet Security.jpg"
ABC = "File.Open(Path, \"\", SW_SHOWNORMAL);Text1 = \"Salam Khobi?\";Text2 = \"Koja Bodi?\";Dialog.Message(\"Notice\", Text1..Text2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);



و یا همین طور میتونین یه الگوریتم تعریف کنین که مثلا حلقه داشته باشه . مثلا همین کد بالا رو 3 بار با حلقه ی for تکرار کنه :




Path = "narm afzar\\Anti viruse\\Avast Internet Security 8.0.1497.376 Final\\Avast Internet Security.jpg"
ABC = "File.Open(Path, \"\", SW_SHOWNORMAL);Text1 = \"Salam Khobi?\";Text2 = \"Koja Bodi?\";for i=1,3 do Dialog.Message(\"Notice\", Text1..Text2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end"
Page.CreateObject(OBJECT_BUTTON, "NewObject", {ButtonFile="button\\button 3.btn",Text="ABC",X=0,Y=0});
Page.SetObjectScript("NewObject", "On Click", ABC);





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

SajjadKhati
05-08-14, 07:41
یه نکته ی مهم و اشتباه برانگیز برای کد دادن به رویداد (رویداد اشیاء) :
اول از همه بگم این تیکه رو مدیون اوستا محسن ام و واقعا ازش ممنونم (ما را از هچل نجات داد )
نگا کنین ، به قول اوستا محسن ، یه پن شیش تا شی Button بزارین تو صفه با نام پیش فرض خودش و یه شی دیگه (مثلا Lable) هم بزارین . تو این لیبل هه ، این کد رو بدین :



Page.SetObjectScript("Button"..j, "On Click", "Dialog.Message(\"\", j);");
j=j+1



تو گلوبال فانکشن یا استارت آپ هم این رو :



j=1


الان تصورتون چیه؟ دکمه ی 1 و 2 و 3 رو بزنین ، برا هر کدوم چه پیامی میده؟
تصور اغلب افرادی که مثل من ان اینه که وقتی دکمه ی Button1 رو بزنن ، پیام 1 رو میده (چون j در اون لحظه ، 1 هه) و وقتی دکمه ی دوم رو میزنین ، پیام 2 و ... رو بده
اما این گونه نیست چگونه هست؟
این جوریه که j مقدار اولیه اش تو دهلیز (نه بطن ) ورود ، 1 بود . بعد خط اول بالا که گفت تو Button..j ، کدی رو ست کن که مقدار j رو بنویسه. ینی چی تا اینجا؟ خوب مقدار j رو جایگزین کنین دیگه ینی اینکه تو Button1 ، پیام ای رو ست کن که عنوان اش ، 1 باشه (البته تا اینجا) . بعد تو خط بعد ، یکی به مقدار j میافزایه خوب حالا دوباره کد بالا رو ترجمه کنین با این مقدار جدید . چی میشه؟ تا اینجاش که رو Button1 ست کنه که انجام شد . هیچ چی . ینی نه اینکه مقدار j برابر 2 شد و از اون طرف هم گفتیم کد رو تو Button..j ست کن ، پس تو Button2 ست میکنه . این که ملومه اون خط کد اجرا شد و برای Button1 کد رو ست کرد و رف پی کارش . وقتی برا دکمه ی 2 ست میکنه کد رو که شما دوباره رو شی Lable کلیک کنین یا اینکه مثلا بعد خط دوم ، کد ست کردن رو مینوشتین. پس تا اینجا مشکلی ندارین؟ خوب حالا ادامه ی نگا رو میندازیم . پس تا اینجا j برابر 2 شد و تو دکمه ی اول یا Button1 این کد رو ست میکنه که وقتی کلیک کنیم ، مقدار j رو نشون میده که برابر چی هست؟ برابر 2
پس اولین باری که کلیک کنین تو دکمه ی اول ، پیام 2 رو میده نه 1 رو . چون قبل اجرا شدن دومین بار کد



Page.SetObjectScript("Button"..j, "On Click", "Dialog.Message(\"\", j);");


، مقدار j یکی اضافه شد . حالا دومین بار وقتی کلیک کنین چی میشه؟ تو Button2 (چون j فعلا 2 هه) ، میگه j رو نمایش بده که قبل از اینکه کد خط بالا اجرا بشه ، مقدار j یکی اضافه میشه پس وقتی دکمه ی 1 یا 2 رو کلیک کنین که دستور داده شد مقدار j رو نشون بده تو هر دوشون ، هر کدوم رو کلیک کنین ، مقدار 3 پیام داده میشه و الی آخر
پس برای رفع این مشکل و موقعی که میخایم یه کد رو ست کنیم که مثلا تو Button1 ، یه پیام خاص رو ست کنه و تو Button2 ، یه پیام خاص دیگه رو ، باید متغییری که نام میبریم ، شماره ی همون دکمه باشه و هر بار ، با اجرای هر دفه کلیک روی دکمه ، اول اون متغییر (تو اینجا مقدار j) اور رایت بشه یا اگه مقدار آرایه ی خاص رو هم میخایم که ست کنه ، به شماره ی همون عضوی که میخایم ، اوررایت اش کنیم اون متغییر رو
یادتون باشه این مقداری که برای اندیس تو آرایه ها اوررایت میشه ، هر بار یکی بهش اضافه میشه ممولا و کلا مقدارش یکی بیشتر یا برابر آخرین عضو آرایه هست اما تو این حالت ما میخایم هر دکمه ای که اجرا میکنیم ، اون متغییر (تو اینجا مقدار j) قبلش برابر شماره ی اون عضوی بشه که ما میخایم
انگار در وهله ی اول ، فقط از همین طریق ، میشه این کار رو انجام دادخوب برا اوررایت کردن هم باید شماره ی اون دکمه رو بگیریم که انگار راهی جز زمان اجرای خود کد ینی داخل پارامتر یا آرگومان سوم ورودی بالا نداریم. پس باید این جوری بنویسین :




Code = "ButtonName = Button.GetProperties(this);ButtonNumber = String.Mid(ButtonName.ObjectName, 7, -1); Dialog.Message(\"\", ButtonNumber );"
Page.SetObjectScript("Button"..j, "On Click", Code);
j=j+1



که موقه اجرای کد ، تو هر دکمه ، اول شماره ی دکمه رو میگیره بعد اونو مینویسه و مستقل از متغییر j عمل میکنه و هر بار هم این متغییر اوررایت و مقدارشم برابر مقدار همون شماره ی دکمه میشه (ینی رو دکمه ی 5 کلیک کنین ، این متغییر 5 میشه و بدش رو دکمه ی 9 کلیک کنین ، بدش 9 میشه) و مثل متغییر j نیست که هر بار فقط یه دونه اضافه بشه
از آقا محسنم بازم تشکر میکنم که بابتش وقت گزاشت

SajjadKhati
05-08-14, 07:45
فارسی نوشتن موقه اضافه کردن متن یه شی :

باید در این مواقع ، FontScript=1 یا همون برابر DEFAULT_CHARSET بگیرین تا فارسی شه بجا زمانی که خودتون تو قسمت Script فونت ، گزینه ی Arabic رو انتخاب میکردین

SajjadKhati
05-08-14, 07:50
یه تجربه ی خنده دار از خودمو بگم که شاید اگه کسی مثّ من فک کرد ، وقتش گرفته نشه : Only the registered members can see the link
اگه خدای نکرده یه وقت مثّ من فک کردین که سمت چپ مساوی یا همون کنار یه متغییر یا آرایه (محتوای متغییر یا مقدار بازگشتی اش که سمت راست مساوی هه منظورم نیست ها) میتونین در ادامه اش یه متغییر دیگه تعریف کنین (که علامت اش دو تا نقطه ی پی در پی هه ینی .. ) یا یک نقطه مثل فرا آرایه یا حتی آرایه ، برا اینکه به قول اوستا حامد بتونین آرایه چن بعدی داشته باشین ، یه متغییر رو از قبل تعریف کنین و با گزاشتن یه نقطه همونو کنار آرایه یا فرا آرایه تعریف کنین ، باید بگم که اشتبا میکردین مثّ من و بر روی آب ، آونگ میکوبیدین Only the registered members can see the link
البته اوستا حامد نگفت با چه زبان هایی میشه آرایه ی چن بعدی نوشت Only the registered members can see the link
مثال های اشتباه :




ABC = {}
ABC.ThisPage = {}
ThisPage = Application.GetCurrentPage();
ABC.ThisPage[1] = "Har Meghdari"




یا




ABC = "A Name"
Motaghaier = "Abc"
ABC..Motaghaier = 5




تو اولی مثال ، ارور شاید نده اما چیزی که شما میخاین هم درس نمیشه و عملیات اچ3 عقیم میمونه Only the registered members can see the link
پس متوجه شدین دیگر؟ حداقل حداقل اش اینه که جز قضیه ی آرایه و فرا آرایه که نقطه میتونین بزارین در سمت چپ تساوی ، برا این قضیه که گفته شد ، اصلا شدنی نیست و نمیتونین بزارین و همینطور ، فرا آرایه و کلا هر آرایه ای تو لوا ابعاد نداره ینی نمیشه مثل داخل اندیس اش که میشه متغییر تعریف کرد ، در سمت چپ تساوی ، نمیشه یه متغییر دیگه تعریف کرد و خودتونو و وقت تونو بخاطر این کار ، به فنا ندین . ما به فنا دادیم و نتیجه ای حاصل نشد Only the registered members can see the link اگه این کار شدنی بود ، یه قدرت و آسونی چن برابری تو لوا برا تعریف آرایه ممکن بود

SajjadKhati
05-08-14, 07:56
ماژول ها :

بازم باید از دو جهت ازت تشکر کنم که سید . هم واسه یاد دادن این مطلب و هم این الگوریتم (ای کاش بیشتر درباره اش توضیح میدادی . فراخونی اش چجوریه؟ من فراخونی کردم array[1][1] اما جواب نداد)
کلا من اگه لیست این تاپیک که وابسته به آموزشای بچه هاست رو لیست کنم ، یه فرا آرایه ی دویست بعدی درمیاد چند بعدش تا حالا شدن گلپسران حامد و جواد و محاسنین (دو تا محسن ) و سید انجمن مون و اوستا محمد (اون یکی منظورمه) . ماشاء ا... یه انجمن هست ، دویست تا دو به دو اسم دارن . سجادین انجمن =3 تا . محسنین انجمن = 2 تا . محمدین انجمن = 2 تا فقط حامدین و جوادین موند
چقد صوبت میکنم؟ بریم سر اسب مطلب



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

یه مثال بزنم (البته همه ی اینا از مثالای بچه هاست و اینم از سید اهل انجمن ، گلپسر محمد همونطور که گفته بودم):



Math = {
add = function (a,b)
return(a+b)
end,
sub = function (a,b)
return(a-b)
end,
mul = function (a,b)
return(a*b)
end,
div = function (a,b)
return(a/b)
end


Math تو مثال بالا ، فرا آرایه ایه که 4 تا عضو داره و هر عضوشم یه تابع هست.
درسته که فرا آرایه ، چند بعدی نیست اما چون داخلش متغییر داره پس آرایه ی معمولی هم نیست و فرا آرایه هست که عضو هاش اینا هستن تو مثال بالا :



Math={add,sub,mul,div}


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



Math.add


که ملوم بود .
** یه نکته اینه که چون تو ماژول ها ، تابع ها میتونن اسم نداشته باشن ، پس باید همونطور که گفته شد ، اسم اون عضو فرا آرایه رو مثل بالا فراخونی کرد و در این حالت هم بهتره برا هر تابع یه مقدار خاص رو برگردونین که میدونین از کلمه ی return باید استفاده شه (حالا بازم مختارین . میتونین بسته به حالات مختلف ، توابع مختلفی بنویسین که اصلا مقداری رو هم برنگردونه)
موفق و پیروز و سربلند باشید

SajjadKhati
12-09-14, 11:37
رشته ها در لوا به دو صورت تعریف میشن :

1- مابین دو علامت " " که بیشتر برای رشته های یک خطی به کار میره.
2- مابین دو علامت [[ ]] که بیشتر برای رشته های چند خطی به کار میره.
مثلا متغییر MyVars زیر که متغییر رشته ای هست رو به دو صورت میشه تعریف کرد :

اول :




MyVars = "MyFunc = {} _DesktopFolder = 100 MyFunc.GetName = \"mohsen\""


دوم :




MyVars = [[
MyFunc = {}
_DesktopFolder = 100
MyFunc.GetName = "mohsen"
]]


قسمت دوم ممولا برا متغییر هایی که تو چن خط تعریف میشن ، کاربرد داره . اما فرقی تو عملکرد با هم ندارن این دو خط


تابع loadstring هم که جزء توابع اصلی برنامه نویسی خود لوا هست ، با ساختار زیر (دو تا پرانتز) که رشته رو دریافت میکنه ، اون رشته رو به متغییر تبدیل میکنه :



loadstring(MyVars)()





برای استفاده از توابع اصلی خود زبان لوا ، تو این لینک بیاین (Only the registered members can see the link)



با تشکر از گلپسر محسن Only the registered members can see the link

SajjadKhati
24-09-14, 13:14
فرا آرایه (MetaTable) :

اول جا تشکر ویژه داره که از گلپسر اشکان کنم که واقعا تو آموزشش بهم کمک کرد :give_rose:
اونی که تو صفحات قبل گفته شد به عنوان فرا آرایه ، فرا آرایه نبود (تا امروز فک میکردم بود) :Love-ssa~! (1): . آرایه ی چند بعدی بود (من فک میکردم هر دو یکی ان . دست اوستا امیر درد نکنه که منو متوجه کرد) :Love-ssa~! (1): بدش بگم که این چیزایی که گفته میشه ، تجربه ی من و راهنمایی اوستا اشکان هه . چون تجربه ام زیاد نیست ، ممکنه جاهایی درست مطلب گفته و جا نندازم یا حتی اشکال یا غلط بگم . هر کی که به درک کامل تر رسید ، اینجا تو این تاپیک کاملش کنه این مبحث رو:give_rose:


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

اگه این دو حالت پیش بیاد ، ینی مثلا یه عضوی از آرایه ای رو بخونیم که nil باشه و براش هم فرا آرایه ست کرده بودیم قبلا ،اون عضو از آرایه بجا اینکه nil رو برگردونه ، آرایه و الگوریتم فراآرایه رو اجرا میکنه
پس فراآرایه در واقع یه جوارایی ساختش ، به ماژول ها شباهت داره (آرایه ای که داخلش تابع تعریف میشه) . این توابع هم این طور نیست که آرگومان و ورودی هاش هر چی دلمون بخاد باشه چون این توابع تو لوا تعریف شده هست و باید با همون تداد آرگومانی این توابع رو بسازیم که تو لوا تعریف شد. لیست این توابع و عملکرداشونو میتونین بصورت کامل تو جدول قسمت پایین این صفه ببینین (Only the registered members can see the link) .(البته همونطور که گفته شد ، چون این توابع ها از پیش تعریف شده ان و تعداد و نوع آرگومان و ورودی هاشون باید همون جوری باشه که تعریف شد و تو اون لیست ، این ویژگی هاش و راهنماش نیومد ، فقط باید از مثال هاش پی ببرین و بازم چون همه ی توابع رو تو اون لینک مثال نزد ، شما میتونین با نوشتن اسم تابع و تو ادامه function example in lua ، تو سایتا سرچ کنین )
فرا آرایه هم با با تابع setmetatable برا یه آرایه تعریف میشه. این تابع ، دو آرگومان داره که تو آرگومان اولش آرایه ای که میخاین فرا آرایه براش ست کنین و نسبت بدین رو مینویسین و آرگومان دوم هم فراآرایه (که معلومه و گفته شد از نوع آرایه هست) رو مینویسین
تا اینجا دید یو آندرستود؟ ندید یو ؟ ببینین یو :Love-ssa~! (1):

بوریم مثالِ سر وقت (بریم سر وقت مثالا . مازندرانی رو باید یاد بگیرینا . از انگلیسی هم واجب تره :Love-ssa~! (1):) :
نگا کنین در واقع تابع setmetatable دو کار میکنه . اول اینکه برا آرایه ، فرا آرایه ست میکنه و دوم اینکه مقدار آرایه ی داده شده (که تو آرگومان اول اش میدادیم) رو برمیگردونه . مثلا تو مثال زیر :





mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)


با این مثال :



mytable = setmetatable({},{})


هیچ فرقی نداره
تو مثال اول ، آرایه ای به عنوان آرایه ی اصلی که که فرا آرایه براش میخاد تعریف شه ، بنام mytable تعریف شد و بدش یه آرایه بنام mymetatable که به عنوان فرا آرایه ای که برا mytable میخاد ست شه و خط بدشو که توضیح دادم.
تو مثال دوم که همونطور که گفته شد ، تابع setmetatable ، آرایه ای که تو پارامتر اولش تعریف شد رو برمیگردونه پس mytable = {} میشه و علاوه بر اینکه فرا آرایه (پارامتر دوم) هم برا این آرایه ست میشه
تا اینجا مثال کلی بود . حالا بریم سراغ یه مثال واقعی :Love-ssa~! (1): :

تابع index__ :
یکی از مهمترین تابع برا ایجاد فرا آرای هست . کارش تعریف فراآرایه ای هست که اگه تو یه آرایه ، موقه فراخونی عضوی از اون آرایه ، اون عضوش nil باشه یا تعریف نشده باشه ، دیگه بجا برگردوندن nil ، اون فراآرایه اجرا میشه .

یه مثال :



mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return mytable[key]
end
end
})


Dialog.Message("Notice", mytable.key1.."\n"..mytable.key2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



اولا تو خط اول گفتیم که تابع setmetatable ، اولین پارامتر یا ورودی اش که آرایه هست رو برمیگردونه پس mytable یه آرایه هست اونم مقادیر و عضوهاش میشن :



mytable = {key1 = "value1"}


دوم اینکه میریم تو خط آخر :Love-ssa~! (1): . خط آخر ، میگه که key1 امین عضو از آرایه ی mytable رو فراخونی کن که خوب تعریف شده هست و مقدارشم برابر رشته ی value1 هه که موردی نداره و در ادامه ی Dialog.Message میگه Key2 امین عضو از آرایه ی mytable رو فراخونی کن . Key2 امین عضو از آرایه ی mytable وجود دارد عایا؟ :Love-ssa~! (1): ندارد عایا . پس چه میشود عایا؟ :Love-ssa~! (1): چون مقدارش برابر nil هه پس فرا آرایه ینی پارامتر دوم setmetatable اجرا میشه.
قبل از ادامه ، اول بزارین نحوه ی تعریف و اصول اولیه ی فرا آرایه گفته شه :
1) همونطور که گفته شد ، برا تعریف تابع ی فراآرایه ، از توابع از پیش تعریف شده ی لوا استفاده میشه (مثل index__ در این مثال و چن تا مثال دیگه در ادامه گفته میشه) که برا استفاده از راهنماش گفته شد
2) تعداد و نوع آرگومان و حتی اسم این توابع دست ما نیست و بازم باید از توی مثال های راهنمای انگلیسی متوجه شین (من انگلیسی ام عالیه :Love-ssa~! (1):) و اغلب آرگومان هاشم راحت میشه حدس زد (تجربه ای متفاوت از حس شیشم:Love-ssa~! (1):) و اغلب تعداد آرگوماناش به تعداد آرایه ی استفاده و درگیر شده هست که اغلب 2 تاست و اغلب هم نوع آرگومان های این توابع هم از نوع آرایه هست و تو اغلب موارد هم آرگومان اول ، اون آرایه ی اصلی مونه که میخایم تغییرات روش انجام بشه
3) برا تعریف تابع تو فراآرایه ها ، مثل مثال بالا عمل کنین ینی اسم تابع مورد نظرتونو برابر بگیرین با کلمه ی فانکشن و بد براش آرگومان تعریف کنین . به این حالت تعریف کنین ، نمیشه ها چون علامت آندرلاین داره و ارور میده :



function __index(mytable, key)


اغلب در اغلب شد :Love-ssa~! (1): خو بَیته مِرِه :Love-ssa~! (1): شام زیاد خوردما ، شکمم پره ، خابم میاد. جاتون پر ، ماکارونی داشتیم:Love-ssa~! (1):
حالا بریم ادامه ی بحث درباره ی آرگومان دوم مثال بالا یا همون فراآرایه اش :
خوب تو این تابع index__ آرگومان هاش این جوریه که (گفته شد که نوع آرگمان اش رو باید تو مثال ها ببینین که از پیش تعریف شده هست و به احتمال بسیار زیاد قابل تغییر نیست) ، وقتی اسم و عضو آرایه فراخونی میشه ، قسمت اسم آرایه ، به یه پارامتر و قسمت عضو آرایه ، به یه پارامتر دیگه تو تابع مورد نظر (در اینجا تابع index__) ارسال میشه . الان تو قسمت Dialog.Message که mytable.key2 فراخونی شد ، اسم آرایه که mytable هه به پارامتر اول تابع index__ که از نوع آرایه هست ارسال شد و اسم key2 به پارامتر دوم این تابع که این بار از نوع رشته هست (ینی key2 بصورت رشته) وارد اونجا میشه که این نوع اش رو که تو اینجا تو این پارامتر ، رشته هست که گفته شد از پیش تعریف شده هست رو تو مثال ها میتونین پیدا کنین
ینی الان تو تابع index__ هر جا mytable نام برده شد ، همون آرایه mytable و هر جا key برده شد ، همون رشته ی "key2" هه منظورش (پس ورودی اول این تابع از نوع آرایه و ورودی دومش از نوع رشته هست)
بقیه دَوِّه فِردا . مِه چِش کور بَیِّه :Love-ssa~! (1):
خا کجا بودیم؟ آها رو کره ی زمین بودیم :Love-ssa~! (1): بریم ادامه مبحث
خوب چون تو mytable.key2 مقدار پارامتر key رشته هه و برابر key2 ینی :



key = "key2"


هست ، و تو شرط تابع index__ این شرط گزاشته شده ، پس تابعش مقدار رشته ای metatablevalue رو برمیگردونه ینی بجا mytable.key2 ، رشته ی metatablevalue برگردونده و چاپ میشه تو Dialog.Message

راستی بجا نوع نگارش تابع تو خط بالا (الگوریتم تابع نه ها) میشه این طور هم نوشت اما من با روش بالا راحت ترم :



mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
Dialog.Message("Notice", mytable.key1.."\n"..mytable.key2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


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



mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, table.maxn(newtable) do
table.insert(mytable, table.maxn(mytable)+1,newtable[i])
end
return mytable
end
})


secondtable = {4,5,6}


mytable = mytable + secondtable
for k,v in ipairs(mytable) do
Dialog.Message("Notice", k.."\n"..v, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


تو توضیحش اینکه اولا تابع add__ دو تا آرگومان داره که اولیش آرایه اول ارسال میشه (قبل علامت جمع یا +) و برا دومیش آرایه ی دوم ارسال میشه (بعد علامت جمع در آرایه ها) و همونطور که از مثال هم معلومه ، تابع اولی که mytable هست و تو خط اول که پارامتر اول setmetatable هست ، تعریف شد، به عنوان پارامتر اول ، به تابع add__ فرستاده میشه و بعد علامت به علاوه که آرایه دوم و secandtable هست ، به عنوان پارامتر دوم به این تابع ارسال میشه
خوب حالا تو خط دهم ینی mytable = mytable + secondtable که مثل مثال قبل نیست که مقدار یه آرایه nil باشه و فراآرایه جاش اجرا بشه. پس چجوریه که با اجرای این خط فراآرایه اجرا میشه؟ فراآرایه ای که برا یه آرایه ست شد ، هر بار که اسم اون آرایه برده شد ، چک میکنه ببینه فراآرایه ای مناسب با عملی که براش تعریف کردیم ، تعریف شد یا نه . اگه تعریف شده بود فقط اجراش میکنه . ینی الان تو مثال بالا ، اگه یه آرایه ای رو فراخونی کنین که مقدارش nil باشه ، ارور میده چون فراآرایه ای که براش تعریف شد بخاطر اینکه تابع مورد نظر (index__) رو نداره ، اصلا اجرا نمیشه در این حالت
خوب حالا با اجرا شدن خط دهم ینی mytable = mytable + secondtable پارامترهای ارسالی که گفته شد و فراآرایه اجرا میشه . تو خط سوم :



for i = 1, table.maxn(newtable) do
table.insert(mytable, table.maxn(mytable)+1,newtable[i])
end


تابع table.maxn(newtable) ، با گرفتن اسم آرایه ، تعداد عضو هاش رو برمیگردونه که همون کار علامت # رو میکنه . ینی میتونین بجا خطوط بالا بنویسین :



for i = 1, #newtable do
table.insert(mytable, #mytable+1,newtable[i])
end


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



mytable = { 1, 2, 3 ,4,5,6}


حالا مثال بدی (فراخونی آرایه . میدونین دیگه منظور همون تغییر عملکرد در فراخونی یا در واقع ست کردن فراآرایه ای برا فراخونی یه آرایه هست) :



mytable = setmetatable({10}, {
__call = function(mytable, newtable)
sum = 0
for i = 1, #mytable do
sum = sum + mytable[i]
end

for i = 1, #newtable do
sum = sum + newtable[i]
end
return sum
end
})


newtable = {10,20,30}
Dialog.Message("Notice", mytable(newtable), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


برا فراخونی (ینی تغییر عملکرد و رفتار فراخونی یه آرایه یا در واقع ست کردن فراآرایه برا تغییر نوع فراخونی) از تابع call__ استفاده میکنیم
خوب همونطور که میدونین این فراآرایه تو مثال بالا ، موقعی اجرا میشه که خط آخر اجرا بشه ینی وقتی که تو پارامتر دوم گفتیم mytable(newtable)
دیگه ورودی این تابع call__ و الگوریتم هایی که توش بکار رفت کاملا مشخصه دیگه :1. (23):
مقدار بازگشتی شم تو این مثال 70 هه
تابع دیگه هم tostring__ هه که برا تغییر عملکرد فراخونی هه که تو مثال سایت بالا اومد

راستی این مثالا از همون لینکی که تو بالا داده شد گرفته شد (از گلپسر سید هم بخاطر معرفی این سایت ممنونم:give_rose:)
امیدوارم براتون مفید بوده باشه این آموزش
بازم دسِّت درد نکنه اوستا اشکان :give_rose:

SajjadKhati
11-12-14, 13:45
چند نکته ی مهم درباره تابع :

1) تابع با چندین مقدار برگشتی :

همونطور هم که تو قضیه ی دیتابیس تو کتاب اوستا حامد گفته شد (تو فیلم دیتابیس sql و ... گفته نشد:Love-ssa~! (1):) ، یه تابع میتونه یا میتونیم کاری کنیم که بجا یه مقدار ، چندین مقدار رو برگردونه که در این صورت باید به تعداد مقدار بازگشتی اون تابع (مثلا فرض میکنیم 3 تا مقدار بازگشتی داره) ، باید به همون تعداد ، متغییر براش تعریف کرد و همه ی متغییرهاشو با ویرگول از هم جدا کرد . مثلا تو مثال زیر :



function func(a,b)
Val1 = a+b
Val2=a*b
Val3 = a-b
return Val1,Val2,Val3
end


Plus,multiplication,Minus = func(200,100)
Dialog.Message("Notice", "a+b = "..Plus.."\na*b = "..multiplication.."\na-b = "..Minus, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تابع بالا که اسمش func هست ، 3 مقدار Val1 و Val2 و Val3 رو برمیگردونه . که موقع فراخونیش هر کدوم به ترتیب ، دقیق تو متغییرهاش (که موقه فراخونی استفاده کردیم) ذخیره میشن ینی مقدار بازگشتی Val1 (که تو مثال بالا که مقدارش 300 هست) تو متغییر Plus و مقدار Val2 (که تو اینجا 20000 هه) تو multiplication و مقدار Val3 (که تو اینجا 100 هه) تو متغییر Minus ذخیره میشه
ینی اولین مقدار بازگشتی تو اولین متغییر تعریف شده و دومین مقدار بازگشتی تابع ، تو دومین متغییر تعریف شده و ... به همین ترتیب ذخیره میشه



2) اجرای تابع (تابعی که در نرم افزار از پیش تعریف شده باشه) داخلی :

همیشه اگه دو تابع داخلی تو در تو باشه ، اول تابع داخلش اجرا میشه و در مرحله ی بعد ، مقدار بازگشتی تابع فراخونی شده برگردونده میشه مثلا :



Dialog.Message("Notice", Dialog.Message("Notice", "Your message here.", MB_YESNO, MB_ICONINFORMATION, MB_DEFBUTTON1), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تو مثال بالا ، اول پیام Your message here داده میشه و بعد مقدار بازگشتی تابع Dialog.Message که عدد هست رو تو پیام دوم میده

3) اجرای چند تابعی که خودمون میسازیم :

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



function Init(a,b,c)
function Sec(a,b)
ABC = a+b+5
return ABC
end
return Sec(a,b)
end


Nm = Init(10,20,30)
Dialog.Message("Notice", Nm, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تو مثال بالا اگه تابع داخلی تره (Sec) تو تابع خارجی تره (Init) فراخونی نمیشد یا برگردونده نمیشد ، تابع Sec هیچ وقت اجرا نمیشد . یه نکته ی دیگه هم اینه که تو این حالت نمیتونیم مستقیما تابع داخلی رو فراخونی کنیم چون ارور میده و تابع داخلی یه تابع رو بصورت مستقیم تشخیص نمیده ینی نمیتونیم این جوری بنویسیم :



function Init(a,b,c)
function Sec(a,b)
ABC = a+b+5
return ABC
end
return Sec(a,b)
end


Nm = Sec(10,20)
Dialog.Message("Notice", Nm, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


4) تابع با ورودی و آرگومان متفاوت :
همونطور که اوستا محمد (سید اهل انجمن خودمون :Love-ssa~! (1):) تو لینک زیر گفت :

Only the registered members can see the link

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



function average(...)
result = 0
local arg={...}
for i,v in pairs(arg) do
result = result + v
end
return result/#arg
end


برا فراخونیش تو دکمه :



Debug.Print(average(1,4,3,5,4).."\r\n"); --result = 3.5
Debug.Print(average(1,4,5,4,4,3,7,8,9,4,2,9).."\r\n"); --result = 5
Debug.Print(average(2,9)); --result = 5.5
Debug.ShowWindow(true);


حالا گلپسران اساتید ، هر جا احساس کردین نیاز به توضیح داره برا این قسمت ، حتما بگین . ممنون :give_rose:

SajjadKhati
11-12-14, 23:24
Coroutine (توقف و ادامه ی اجرای کد در لوا) :

اول باید تشکر کنم از اوستا اشکان و اوستا محمد که تو ارائه این بحث کمک کردن (ممنان گلپسران) :give_rose:
این کار رو با کمپوننت اتوات هم میشه انجام داد ولی راه اجرای مستقیمش تو لوا همینه
درباره اش که دیگه توضیح نمیخاد . ها؟ کارش اینه که تو کوروتین ای که تعریف کردیم ، میتونیم هر وقت دلمون خاست ، اجرای کد رو متوقف کنیم و هر وقت دلمون خاست ، ادامه کد رو اجرا کنیم . حالا مستقیم میریم سر اصل Matlab :Love-ssa~! (1): (جان من الان ساعت دقیق 2:45 دیقه هه . ببینین ساعت چن این کتاب تموم میشه :Love-ssa~! (1):) . اول دونه دونه ی توابع کورتین توضیح داده میشه بد میریم سر وقت مثالا . فقط اوایلش شاید یه کم گیج کننده باشه پس خوب بگوشین :Love-ssa~! (1): . میتونین توضیح رو از اینجا هم ببینین :



Lua 5.1 Reference Manual (Only the registered members can see the link-coroutine.create)


coroutine.create : برای تعریف کوروتین تا اینکه تابعی رو تو وسطش یا هرجای دیگه اش متوقف کنیم ، اول باید کوروتین رو ایجاد کنیم با همین تابع . به عنوان ورودیش ، اون تابعی که میخایم کد وسطش توقف پیدا کنه رو میدیم . کار اصلی این تابع اینه که به تابع coroutine.resume که توضیح داده میشه درباره اش ، میگه که آخرین باری که کد توقف پیدا کرد ، تو کدوم خط بود. یادتون باشه ، بعد از تعریف یا فراخونی این تابع coroutine.create ، دیگه تابع coroutine.resume نمیدونه از کجا شروع کرده بود و کدها رو از اول اجرا میکنه ینی اگه هر وقت از این تابع coroutine.create استفاده کنیم ، بعد از اون ، اولین باری که از تابع coroutine.resume استفاده میکنیم (اولین بارها نه اینکه چندین بار) ، تابعی که تو ورودی coroutine.create داده بودیم تا کدها رو متوقف کنیم هر وقت دلمون خاست ، این تابع از اول کداش اجرا میشه
تابع coroutine.create هم مقداری از نوع نخ یا thread رو برمیگردونه . که من نفمیدم این دقیقا چیه اما مقدار بازگشتیش اصلا برامون مهم نیست ولی هر چی که هست ، این قدو میدونیم حاوی اطلاعات این هست که آخرین بار تا کجا کدمون اجرا شده بود و برا تابع coroutine.resume میفرسته این اطلاعاتو تا بفمه از کجا باید ادامه بده . اما همونطور که گفته شد ، این تیکه آخر برا اطلاعات عمومی هه :Love-ssa~! (1):



coroutine.resume : از وقتی که تابع coroutine.create تعریف شد تا زمانی که اون تابع مورد نظرمون که به عنوان ورودی تو coroutine.create دادیم ،تموم نشد ، این تابع coroutine.resume هر وقت که اجرا بشه ، ادامه ی کدهای اون تابع رو اجرا میکنه منتها چندین نکته ی اساسی مهم داره :
اول اینکه اگه تابع برا ایجاد کوروتین قبل استفاده از هر تابعی از توابع این خانواده من جمله coroutine.resume یا coroutine.yield تعریف نشده باشه و یا به عبارتی تابع coroutine.resume یا coroutine.yield ، اول از همه تعریف شده باشن ، ارور میده برنامه
دوم اینکه زمانی که اون تابع مورد نظرمون که به عنوان ورودی تو coroutine.create دادیم ،تموم بشه ، دیگه تابع coroutine.resume نمیتونه کدها یا همون تابع ی که تو کوروتین دادیم رو دوباره اجرا کنه (ینی فقط یه بار اجرا میشه) و اگه بخایم دوباره اجرا کنه ، باید قبل از coroutine.resume ، دوباره همون تابع coroutine.create رو فراخونی کنیم تا دوباره ساخته شه
سوم و مهم ترین نکته اینکه بعد از هر بار فراخونی یا ایجاد کوروتین با تایع coroutine.create ، اولین بار بود که coroutine.resume رو فراخونی کردیم ، در این صورت ورودی های اولین تابع coroutine.resume که فراخونی شد (در مثال لینک بالا Val1) (تا وقتی که تابع مورد نظرمون تمام نشد یا coroutine.create دوباره فراخونی نشد) همون برابر ورودی های توابع مورد نظرمون استفاده میشن . اما از اون به بعد ینی تو فراخونیهای بعدی تابع coroutine.resume (تا وقتی که تابع مورد نظرمون تمام نشد یا coroutine.create دوباره فراخونی نشد) ، ورودی های coroutine.resume (در صورت وارد کردن ورودی . در مثال لینک بالا همون Val1) به عنوان خروجی یا همون مقدار (سمت چپ مساوی) تابع بعدی coroutine.yield ذخیره میشه . مقدار بازگشتی خود coroutine.resume هم بولین (true در صورتی که کدها تموم نشده باشه تو تابع موردنظر و یا ارور نداده باشه وگرنه false هست) به اضافه ی یه مقدار دیگه (ینی تابعی هست که چندین مقدار رو برمیگردونه مثل مثال پست بالا) که اگه false باشه ، متن ارور رو میده (در صورتی که تموم شده باشن کدها و کاملا اجرا شده باشن ، متن cannot resume dead coroutine رو برمیگردونه) و اگه true باشه ، ورودی های تابع coroutine.yield به عنوان متغییرهای تابع coroutine.resume (سمت چپ مساوی این تابع) ذخیره میشن و خوب ملومه که تعدادشونم به تعداد ورودی های تابع coroutine.yield وابسته هست
در کل ینی در واقع بصورت ضربدری میشه رابطه ی بین دو تابع coroutine.resume و coroutine.yield . در صورت داشتن ورودی ، ورودی تابع coroutine.yield به عنوان خروجی coroutine.resume ذخیره میشه و ورودی تابع coroutine.resume (البته با شرایطی که تو بالا گفته شد ینی اولین تابعش نه که به عنوان ورودی های توابع اصلی در نظر گرفته میشه تا آخرین لحظه ای که کدها تموم شن یا کوروتین جدید تشکیل شه) هم به عنوان خروجی coroutine.yield ذخیره میشه
نکته ی چهارمم اینکه پس ورودی و خروجی این دو تا تابع باید با هم برابر باشن. ینی ورودی تابع coroutine.yield با خروجی تابع coroutine.resume و همینطور ورودی تابع coroutine.resume با خروجی تابع coroutine.yield بصورت دو به دو یا ضربدری با هم برابر باشند . اگه این طور نباشه یا در کل هر حالتی پیش بیاد (مثلا نوع متغییرمون اشتباه باشه به عنوان ورودی برا تابع اصلی کوروتین) که در صورت عادی باعث بوجود اومدن ارور بشه ، این حالت تو کوروتین ها بجا ارور ندادن ، اصلا اون قسمت یا کل تابع کوروتین (منظورم همون تابع اصلی مورد نظرمون هست) اصلا اجرا نمیشه (که ارور بده یا نده) پس حواستون رو به سمت دقت جمع کنین :Love-ssa~! (1):
توضیح مقدار ورودی اش هم که سایت بالا اینجوری گزاشت :



coroutine.resume (co [, val1, ···])


اولا هر جا تو برنامه نویسی علامت پرانتز یا opt که مخفف optional هست یا علامت سه تا نقطه ... ینی دلخواهی که خاستین پر کنین یا نکنین اما تو اینجا اولین آرگومان یا ورودی اش که co هست منظورش اینه که تابع کوروتین رو که قبلا ایجاد شده بود با coroutine.create رو هدف قرار بده :Love-ssa~! (1): (تو تابع یا در واقع کوروتین co) ینی تو این تابع که اسمش co هست . تو ورودی بعدی coroutine.resume که دلخواهی بود ، همونطور که گفته شد ، برا بار اول که coroutine.resume فراخونی شد ، این قسمت پارامترش که تو مثال بالا گفت دلخواهی هست ، همون به عنوان پارامترهای ورودی تابع کوروتین یا اصلی ما استفاده میشه اما در دفعات بعدی (تا تموم شدن یا فراخونی یا ایجاد تابع کوروتین یا coroutine.create) که تابع coroutine.resume تکرار بشه ، همین پارامتر که ورودی اش به عنوان خروجی تابع coroutine.yield استفاده میشه ، بسته به نکته ی چهارم تو بالا (بسته به تعداد متغییر یا خروجی تابع coroutine.yield بعدی) ، میتونیم این بار ورودی coroutine.resume رو هر چی که دلمون خاست بدیم
شاید مثل من قاتی کرده باشین مطالب رو :Love-ssa~! (1): اما این مطالب رو یادتون فقط باشه تو مثال قشنگ متوجه میشین



coroutine.running : تست نکردم:Love-ssa~! (1):



coroutine.status : مقدار رشته ای رو برمیگردونه. اگه کوروتین با تابع coroutine.yield متوقف بشه ، رشته ی "suspended" و اگه ارور بده و یا تموم شده باشه کوروتین ما (تابع اصلی مون تا خط آخر اجرا شده باشه) ، رشته "dead" رو برمیگردونه
به عنوان ورودی شم اسم کوروتین (اسم تابع coroutine.create) رو میزاریم که وضعیت اون کوروتین رو مشخص کنه . رشته "running" هم موقع اجرای تابع برگردونده میشه و رشته ی "normal" هم دقیق نمیدونم .



coroutine.wrap : که دقیق کار همون تابع coroutine.creat رو میکنه اما انگار مقدار بازگشتیش ، همون مقدار تابع coroutine.resume هست بدون مقدار بولین اش . گفتم انگار چون با این تابع کار نکردم اما بازم انگار :Love-ssa~! (1): آسون تر از اولی باشه . بازم نمیدونم



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





آخیش . تازه رسیدیم سر مثال اول ::Love-ssa~! (1):
مثال ها هم از این سایت زیر گرفته شد (سایت خیلی خوبی هه ها برا کلیه ی زبان ها آموزش داره) :

Lua - Coroutines (Only the registered members can see the link)

خوب بتوجهین گلپسران . تو گلوبال فانکشن این کد رو بزارین: :Love-ssa~! (1):




function f(value1,value2)
tempvar3 =10;
Dialog.Message("Notice", "coroutine section 1\n".."value1 = "..value1 .."\nvalue2 = "..value2 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
tempvar1 = coroutine.yield(value1+1,value2+1);
tempvar3 = tempvar3 + value1;
Dialog.Message("Notice", "coroutine section 2\n".."tempvar1 = "..tempvar1 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2);
tempvar3 = tempvar3 + value1;
Dialog.Message("Notice", "coroutine section 3\n".."tempvar1 = "..tempvar1 .."\ntempvar2 = "..tempvar2 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
return value2, "end"
end


تو یه رویداد دیگه که میخاین کد اجرا بشه ، این کد رو بزارین :



co = coroutine.create(f)
Res1,A1,A2 = coroutine.resume(co, 3, 2)
if Res1==true then
Dialog.Message("Notice", "main\n"..A1 .."\n"..A2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res2,B1,B2 = coroutine.resume(co, 12,14)
if Res2==true then
Dialog.Message("Notice", "main\n"..B1 .."\n"..B2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res3,C1,C2 = coroutine.resume(co, 5,6)
if Res3==true then
Dialog.Message("Notice", "main\n"..C1 .."\n"..C2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res4,D1 = coroutine.resume(co, 10,20)
if Res4==false then
Dialog.Message("Notice", "main\n"..D1 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


اول بگم که این کد بالا رو میتونین این جوری هم بنویسین :



co = coroutine.create(function (value1,value2)
tempvar3 =10;
Dialog.Message("Notice", "coroutine section 1\n".."value1 = "..value1 .."\nvalue2 = "..value2 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
tempvar1 = coroutine.yield(value1+1,value2+1);
tempvar3 = tempvar3 + value1;
Dialog.Message("Notice", "coroutine section 2\n".."tempvar1 = "..tempvar1 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2);
tempvar3 = tempvar3 + value1;
Dialog.Message("Notice", "coroutine section 3\n".."tempvar1 = "..tempvar1 .."\ntempvar2 = "..tempvar2 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
return value2, "end"
end
)


Res1,A1,A2 = coroutine.resume(co, 3, 2)
if Res1==true then
Dialog.Message("Notice", "main\n"..A1 .."\n"..A2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res2,B1,B2 = coroutine.resume(co, 12,14)
if Res2==true then
Dialog.Message("Notice", "main\n"..B1 .."\n"..B2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res3,C1,C2 = coroutine.resume(co, 5,6)
if Res3==true then
Dialog.Message("Notice", "main\n"..C1 .."\n"..C2 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


Res4,D1 = coroutine.resume(co, 10,20)
if Res4==false then
Dialog.Message("Notice", "main\n"..D1 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end



اولا فرق شون تو اسم تابع هست که اولی اسمش f و تو دومی اسم نداره . دوما فرق شون اینه که تو کد اول که جدا هستن ، اگه تابع رو جداگانه خودمون فراخونی کنیم (بدون کوروتین منظورمه ها) تا اونجایی که ربطی و تابعی از کوروتین (coroutine.yield) نداره ، سالم اجرا میشه قبلش و بهش که رسید ارور میده اما تو کد دوم کلا اجرا نمیشه (احتمالا تو روش دوم همون قضیه ی پست بالا باشه که گفته شد (تابع در تابع))
برا اینکه شماره گزاری راحت تر شه ، دومین کد رو میگم:
خط اول که بسم ا... کوروتین ینی ساخت کوروتین هه که ورودی شم تابع مورد نظر مونو میزاریم توش (توضیح کاملش بالا داده شده) . اسمشم co گزاشتیم. پس این co کوروتینی هست که حاوی تابع اصلی مونه
حالا یهو پرش میکنیم روی خط چارده (بخاطر این اف چهارده آواتارم:Love-ssa~! (1):) چون تابع که خودش به خودی خود اجرا نمیشه و با فراخونی اجرا میشه که بده تابع ینی تو خط 14 فراخونی شد :



Res1,A1,A2 = coroutine.resume(co, 3, 2)


این تابع که میدونین برا اجرا یا ادامه ی اجرای تابع داخل کوروتین هست . از ورودی شروع میکنیم ینی فعلا به متغییر یا خروجی این تابع کاری نداریم . ورودی اش همونطور که گفته شد ، اولین پارامترشو اسم کوروتین مونو برا اجرای کدای توابعش میزاریم که co بود . پارامتر بدی که با ویرگول ازش جدا میشه ، این جوری تعیین میشه که اول میگیم آیا این تابع coroutine.resume ، اولین اجرا بعد از آخرین اجرای تابع coroutine.create هست؟ یعنی coroutine.create که فراخونی شد ، بعد از اون ، اولین باره که تابع coroutine.resume فراخونی شد؟
آری دیگر . ها؟ خوب پس اگه اولین باره ، پارامترهای بعدی این تابع coroutine.resume (که تو اینجا 3 و 2 هه) همون به عنوان ورودی یا آرگومان تابع اصلی مون (تابعی که تو ورودی coroutine.create گزاشتیم) هست و تا آخر هم هر چند بار که تابع coroutine.resume اجرا شه اما ورودی تابع اصلی مون همین مقدار اولین باری که تو تابع coroutine.resume بود ، ثابت میمونه و تغییر نمیکنه (البته تا زمانی که کدهای تابع اصلی مون تموم نشه که در این صورت اصلا تابع coroutine.resume کار نمیکنه و یا اینکه دوباره تابع coroutine.create اجرا نشه که در این صورت ، باز اولین تابع coroutine.resume ای که بعد از coroutine.create اجرا شه ، به عنوان ورودی اصلی تابع اصلی مون در نظر گرفته میشه)
اگه بازم پیچیده شد این تیکه ، نگران نباشین ، پلین در خدمت تان اسب :Love-ssa~! (1): ادامه رو گوش کنین متوجه میشین
پس تا حالا چی شد؟ کوروتین تو خط اول تشکیل شد و اسمشم شد co و تو خط 14 ام ، با تابع coroutine.resume گفت که کدهای کوروتین co با متغییر اولش که 3 هست (ینی Value1 تو تابع اصلی مون برابر 3 میشه) و متغییر دومش (ینی Value2=2 میشه) اجرا بشه
درباره خروجی این تابع تو خط چهارده بعدا صوبت میکنیم . چرا؟ چون بصورت ضربدری بود قضیه دیگه
پس الان دستور اجرای کد رسیده و کوروتین اجرا میشه . خط دوم که هیچ چی . میرسیم خط سوم که :



Dialog.Message("Notice", "coroutine section 1\n".."value1 = "..value1 .."\nvalue2 = "..value2 .."\ntempvar3 = "..tempvar3 , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


خروجیش که ملومه Value1 که 3 بود و Value2 که 2 بود و Value3 هم که تو خط دوم 10 بود
تو خط چهارم ، اول این خطه اجرا میشه و بعد از اون دیگه متوقف میشه . تابع coroutine.create هم که تا این خط رو برا نشونه به تابع coroutine.resume بعدی (این تو خط چهارده نه ها ، خط 19 منظورمه) میفرسته که از اون خط به بعد اجرا شه :



tempvar1 = coroutine.yield(value1+1,value2+1);


پارامترهاش هم که ملومه دیگه . اولین پارامترش 4 و دومین اش میشه 3 . قبلا چجوری گفته شد؟ بصورت ضربدری هستش رابطه شون دیگه . ها؟ ینی پارامترهای تابع coroutine.yield تو متغییر یا خروجی coroutine.resume ذخیره میشن و برعکس. بازم خروجی یا متغییر اینو بعدا حساب میکنیم که چی درمیاد . فعلا این خط تمام شد و کوروتین co کداش متوقف شد و میریم تو خط چهاردهم که بودیم . تو اینجا 3 تا متغییر داریم که به ترتیب Res1 و A1 و A2 هه . گفته شد که تابع coroutine.resume دو نوع متغییر رو ذخیره میکنه که اولیش بولین هه و اگه تابع اصلی مون تموم نشده باشه ، true وگرنه false رو برمیگردونه . پس تابع اصلی یا کوروتین مون که کداش تموم نشد (مکث کردن منظورم نیست ها) پس تو Res1 مقدار true ذخیره میشه. بقیه ی مقادیرها هم که گفته شد همون ورودی تابع coroutine.yield که به ترتیب اولی 4 و دومی 3 بود ، توی خروجی یا متغییرهای بعدی تابع coroutine.resume به ترتیب ذخیره میشن ینی مقدار A1 میشه برابر با اولین مقدار پارامتر coroutine.yield که 4 بود و دومی هم به همین ترتیب میشه A2 برابر 3
خوب همونطور که قبلا هم گفته شد ، پارامترهای ورودی تابع coroutine.yield کاملا دلخواهی هست که مقدارشو چی بزاریم و اصلا ربطی به هیچ الگوریتم و مقادیر دیگه ای نداره اما اینکه کلا بزاریم یا چن تا بزاریم ، برمیگرده به خروجی تابع coroutine.resume (که الان دیدین) ینی الان تو مثال بالا اگه میخاین به تابع coroutine.yield تو خط چهارم مقدار ندین (البته اگه مقدار نمیدین ، علامت پرانتز رو به عنوان تابع پاک نکنین) باید تو خط چهاردهم ، متغییرهای A1 و A2 رو برای coroutine.resume ست نکنین
نکته ای که اینجاست اینه که درسته پارامتر و آرگومان های coroutine.yield دلخواهی هه اما چون تابع اصلی مون وسط کار متوقف میشه ، این قابلیت هست که بجای مقدار بازگشتی اون تابع ، از این آرگومان و ورودی این تابع بجاش استفاده کرد
بعد تو خط 15 که شرط برقراره و تو خط 16 هم که مقادیرها نشون داده میشن که تازه توضیح داده شد
حالا خط 19 اجرا میشه :



Res2,B1,B2 = coroutine.resume(co, 12,14)


مثل قبلی هه کلا اول با خروجی یا متغییرهاش کاری نداریم . مستقیم سر وقت ورودی. قبلشم خوب ملومه دیگه میگه کوروتین co رو کداشو ادامه بده . قبلشم تابع coroutine.create ، ملوم کرد براش که از کجا شروع کنه که همون خط زیر آخرین تابع coroutine.yield ای بود که اجرا شده بود ینی از خط 5 ام شروع به اجرا شدن میکنه منتها اول ورودی تابع coroutine.resume باید تو خط 19 (خط بالا) ملوم شه که چی کاره ان؟ همه کاره ان :Love-ssa~! (1): برا تعیین این ورودی چه سئوالی از خودمان میپرسیدیم گلپسران؟ تکرار کنم عایا؟ :Love-ssa~! (1): آیا این تابع coroutine.resume ، اولین اجرا بعد از آخرین اجرای تابع coroutine.create هست؟ یعنی coroutine.create که فراخونی شد ، بعد از اون ، اولین باره که تابع coroutine.resume فراخونی شد؟ آیا ازدواج با مرد عنکبوتی کار خوبی ست ؟ :Love-ssa~! (1):
جواب خیر است دیگر بالام جان ها دیگر . ها؟ این الان دومین باره که اجرا شد تابع coroutine.resume بعد از آخرین اجرای تابع coroutine.create که تو خط اول بود . یه بار coroutine.resume تو خط چهارده و الان دومین بار تو خط 19 اجرا شد
جواب اگه منفی هه ، در این صورت ، ورودی های تابع coroutine.resume به عنوان خروجی یا متغییر تابع coroutine.yield قبلی (در اینجا قبلی اش تو خط 4 هه دیگه) استفاده میشه . اگه مثبت بود که مثل بالا ، ورودی هاش به عنوان ورودی تابع اصلی یا کوروتین مون استفاده میشه.
پس الان ورودی توابع مون یعنی Value1 و Value2 هم همون قبلی ان دیگه یعنی به ترتیب 3 و 2
خوب الان تو خط 19 ، ینی تو تابع coroutine.resume ، اولین ورودیش 12 و دومیش 14 هه . گفته شد این ورودی ها تو متغییر یا به عنوان مقدار بازگشتی تابعcoroutine.yield قبلی (که تو خط 4 آخریش بود) ذخیره و بازگردونده میشه . خط 4 هم یه متغییر داره فقط . اسمشم tempvar1 هست . پس ملومه که فقط جای اولین متغییر رو داره ینی تو tempvar1 فقط عدد 12 برگردونده یا ذخیره میشه . اگه این تابع دو متغییر داشت ، تو دومیش 14 هم ذخیره میشد
حالا ادامه ی خط هاش اجرا میشه
تو خط 5 ، مقدار Value1 که 3 بود و مقدار tempvar3 هم که 10 بود که جمعش میشه 13 و تو tempvar3 ذخیره میشه
تو خط 6 هم که مقدار این دو تا رو فراخونی کرد که اشاره شد
تو خط 7 ام ، مثل قبلا ، اول coroutine.yield اجرا میشه و ورودی هاش تو خروجی تابع coroutine.resume ای که باعث اجراش شده بود ینی تابع خط 19 ذخیره میشه . مقادیر ورودیهاش که ملومه . تو خط 7 ام ، اولین آرگومان ورودیش میشه 5 و دومیشم 1 دیگه (Value1=3 و Value2=2 بود دیگه)
دوباره میرسیم به خط 19 . مثل قبلی Res2 مقدارش true میشه و B1 برابر اولین آرگومان آخرین تابع coroutine.yield (که تو خط 7 اجرا شده بود) میشه و ... ینی B1 برابر 5 و B2 برابر 1 میشه
دیگه خط 20 و 21 که ملومه
حالا خط 24 اجرا میشه . اون سئوالم از خودتون بپرسین ، جوابش میشه خیر :Love-ssa~! (1): پس پارامتر اول تابع coroutine.resume تو این خط ، تو اولین متغییر coroutine.yield تو خط 7 ام ذخیره میشه و دومی آرگومان هم همینطور هم همینطور پس مقدار متغییر tempvar1 تو خط 7 ام میشه 5 و tempvar2 میشه 6
تو خط هشتم ، آخرین بار tempvar3 برابر 13 بود . Value1 هم که تغغیری نکرد و 3 هست که جمع میشن و 16 تو tempvar3 ذخیره میشه
خط نهم هم که ملومه . تابع دو مقدار رو برمیگردونه که Value2 برابر 2 بود و یکی دیگه هم که رشته ی "end" هه
حالا دوباره میره رو خط 24 . درسته که علی الظاهر تابع اصلی مون به آخر رسید اما این بار ، مقدار true رو باز برمیگردونه (دفعات بعد مقدار false رو برمیگردونه) چون این بار تابع coroutine.resume خط 24 تونست کدهای کوروتین رو اجرا کنه . پس تو متغییر Res3 مقدار true ذخیره میشه. حالا که تابع تموم شد ، بجای ورودی تابع coroutine.yield ، مقدار بازگشتی تابع اصلی مون که Vlue2 و "end" هستش به ترتیب تو متغییر coroutine.resume خط 24 ذخیره میشه پس C1 میشه 2 و C2 میشه "end"
حالا خط 29 اجرا میشه :



coroutine.resume(co, 10,20)


اجرا میشه و مقدار بازگشتیش چیه؟ چون کامل کوروتین اجرا شد ، اولین مقدار ینی Res4 برابر false میشه و متغییر D1 هم توش متن ارور که موقه تموم شدن این پیام میده ، ذخیره میشه : "cannot resume dead coroutine"
خروجی کلی این مثال میشه :



coroutine section 1 3 2 10
main 4 3
coroutine section 2 12 13
main 5 1
coroutine section 3 5 6 16
main 2 "end"
main "cannot resume dead coroutine"

خسته که نشدین؟ زیاده؟ تهش به اندازه یه کتابه دیگه . به جون شما ، مِردال شدم رفت . اون موقه کی بد ، الان کی هه . ساعت 10 و ربه:lol:





مثال دوم (تکرار با استفاده از کوروتین) :

به جون شما این آخرین مثاله .:Love-ssa~! (1):



function getNumber()
local function getNumberHelper()
co = coroutine.create(function ()
coroutine.yield(1)
coroutine.yield(2)
coroutine.yield(3)
coroutine.yield(4)
coroutine.yield(5)
end)
return co
end
if(numberHelper) then
status, number = coroutine.resume(numberHelper);
if coroutine.status(numberHelper) == "dead" then
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
end
return number
else
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
return number
end
end


for index = 1, 10 do
Dialog.Message("Main", index.."\n"..getNumber(), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


میرویم به خط 27 چون بقیه تابع ان تا فراخونی نشن ، اجرا نمیشن
index که شمارنده هه . 10 بار تابع getNumber() همراش فراخونی میشه . که هر 10 بار رو چک میکنیم (3 بارشو چک میکنیم ، بقیه که ملوم میشه :Love-ssa~! (1):)
لازم به ذکر است (اخبار نیستا :Love-ssa~! (1):) که طبق مثال پست بالا ، تو خط 27 چون تابع getNumber() تو تابع Dialog.Message فراخونی شد ، اول تابع getNumber() اجرا میشه و بعد اگه مقدار بازگشتی داشت ، تو تابع Dialog.Message ذخیره میشه
بار اول :
بازم لازم به ذکر است (بازم اخبار نیستا:Love-ssa~! (1):) که طبق مثال پست بالا ، چون تابع getNumberHelper داخل تابع getNumber هست پس تا از داخل تابع getNumber ، تابع getNumberHelper فراخونی نشه ، تابع getNumberHelper اجرا نمیشه . پس بازم میرسیم مستقیم به خط 12 اون شرطه . میدونین که یه متغییر خالی تو شرط بیاد (تو خ 12منظورمه که متغییر numberHelper هه) ینی اینکه اون متغییر مقدار داشته باشه یا مخالف nil باشه ، شرط اجرا میشه . بار اول هم چون این متغییر numberHelper پوچ یا nil هست ، پس قسمت else اجرا میشه ینی خط 20 ام
تو خط 20 ام ، تابع getNumberHelper() فراخونی شد و تو متغییر numberHelper ذخیره شد . معنی اش دیگه ملومه ینی هر وقت numberHelper اسمش برده شد ، ینی اسم getNumberHelper() برده شد و این تابع رو اجرا کنه
پس با این اوضاع ، میره به خط دوم و تابع getNumberHelper() رو اجرا میکنه . و خط سوم هم کوروتین رو با اون تابعی که بهش داده شده ایجاد میکنه بازم با نام co اما کوروتین اجرا نمیشه ها . یادمون باشه که کوروتین فقط با دستور تابع coroutine.resume اجرا میشه . سر آخر هم تو خط 10 ، تابع getNumberHelper() خود کوروتین co رو برمیگردونه . خوب توی متغییرها هم که اگه تابع سمت راست ، مقدار بازگشتی داشت که تو سمت چپ یا همون متغییرش اون مقدار ذخیره میشه اما اگه مقدار بازگشتی نداشت (البته جدای از قضیه ی thread که برمیگردونه) ، وقتی اون متغییر رو فراخونی کنیم ، انگار خود اون تابع رو اجرا کردیم. از اون طرف هم متغییر numberHelper تابع getNumberHelper() و خود تابع getNumberHelper() کوروتین co رو برمیگردونه پس در واقع متغییر numberHelper کوروتین co رو برمیگردونه ینی در واقع اندر واقع هر وقت متغییر numberHelper رو فراخونی کردیم منظورمون همون کوروتین co هست .
خوب پس تا اینجا این شد که تو خط 20 ، چون getNumberHelper() فراخونی شد و ادامه ی قضیه های پشت سرش که گفته شد ، باعث ایجاد کوروتین شد
حالا خط 21 :



status, number = coroutine.resume(numberHelper);


مثل قضیه ی بالا ، اول ، متغییرهاشو میزاریم کنار و بعدا حساب میکنیم . اول به آرگومان های ورودیش میرسیم که نداره :Love-ssa~! (1): میگه تو کوروتین numberHelper که گفته شد همون کوروتین co (تو خط سوم) هست ، رو اجرا کنه (چون اولین اجراشه بعد از ایجاد آخرین کوروتین) و آرگومان هم که نداره ینی این معنی رو میده که تابع coroutine.yield مربوط بهش ، خروجی یا متغییر برا ذخیره شدن نداره .
پس تابع کوروتین ینی خط چهارم اجرا میشه. همینجا خِرشو میگیره و کد متوقف میشه :Love-ssa~! (1): و ورودی coroutine.yield تو خط 4 ام که 1 هست مثل قبلی ، تو خروجی یا متغییر آخرین تابع coroutine.resume که تو اینجا تو خط 21 بود ذخیره میشه منتها قبلش تو اولین متغییرش مقدار بولین که true هست ذخیره میشه پس متغییر status مقدارش true و متغییر number مقدارش 1 میشه . تو خط 21 هم که کلمه return اومد که مال تابع getNumber() هست ینی این تابع ، مقدار number که 1 هست رو برمیگردونه. و بعد تو خط 27 مینویسه . حالا حلقه for برا دومین بار تکرار میشه و دوباره تابع getNumber() رو فراخونی میکنه . دوباره هم توابع داخلی یا getNumberHelper() اجرا نمیشه چون فراخونی نشد و مستقیم سر خط 12 میره و شرط رو چک میکنه که این بار این شرط برقراره ینی متغییر numberHelper مقدارش مخالف nil هست که مقدارش همون تابع رو تو خودش ذخیره میکنه پس مستقیم به خط 13 میره :



status, number = coroutine.resume(numberHelper);


میگه کوروتین co اجرا بشه ادامه اش . پس خط 5 اجرا و متوقف میشه بعدش . بازم status که true هست و number هم که 2 میشه (دیگه چندین بار توضیح داده شد:Love-ssa~! (1): خسته شدم . نشدم . دشمن شد:Love-ssa~! (1):) حالا میره خط 14 و وضعیت این کوروتین رو میبینه تموم شد یا نه (dead هست یا نه) . اگه تموم شده باشه ، میگه کوروتین رو دوباره ایجاد کن که تو خط 15 میتونین ببینین که نوشته numberHelper = ()getNumberHelper که همونطور که تو خط بالاتر توضیح داده شد ، ینی فراخونی کوروتین و ینی ایجاد کورتین co جدید (اگه کوروتین تموم شده باشه اجرای توابعش ، دیگه تابع coroutine.resume نمیتونه اجراش کنه جز اینکه دوباره کوروتین فراخونی یا ایجاد شه)
یادمون باشه اگه کوروتین رو قبل تمام شدن تابع کوروتین ، دوباره ایجاد کنیم ، تمام اطلاعات ریست میشه و تابع coroutine.resume ای که بعد از این فراخونی بشه ، این تابع کوروتین رو از اول اجرا میکنه و همینطور آرگومان هاش هم به عنوان آرگومان های کوروتین در نظر گرفته میشه چون اولین اجرای تابع coroutine.resume بعد از ایجاد کوروتین جدید هست که اینا قبلا گفته شد
به هر حال اینجا تو خط 14 هنوز شرط برقرار نیست چون اجرای کوروتین تموم نشد پس میره به خط 18 که return number هست که ینی تابع getNumber() مقدار number که الان 2 هست رو برمیگردونه و تو خط 27 چاپ میکنه
حالا تابع for سومین بار اجرا میشه و تا 5 امین بار همین حالت دوم که تو بالا گفته شد اجرا میشه
دفعه ی پنجم فرقش اینه که این بار چون شرط اتمام اجرای تابع یا "dead" برقرار میشه ، همون اولش تو خط 15 :



numberHelper = getNumberHelper()


مثل بار اول تابع کوروتین دوباره اجرا میشه که باعث میشه تابع coroutine.resume کدهای کوروتین رو دوباره اجرا کنه

گلپسران اگه اشکالی چیزی دیدین ، خوشحال میشم راهنمایی کنین
موفق و پیروز و سربلند باشین . مِرِه خو بَیته :Love-ssa~! (1): :give_rose: :11():

SajjadKhati
16-12-15, 10:00
تو این آموزش رسمی کتابی نوشته شد :1. (26):

1) بلاک ها (Block) :
در واقع هر شرط و حلقه و کلا هر چیزی که به کلمه ی کلیدی end ختم شود را میشود یک بلاک نامید
اما منظور ما در اینجا از بلاک ، شرط و ... نیست . ساختاری به این شکل است :



do
--body
end



معمولا این نوع بلاک ، اغلب برای تعریف متغییر محلی بیشترین کاربرد را دارد.



2) متغییر محلی (local variable) :
متغییر محلی ، متغییری ست که اگر در بلاک ای تعریف شده باشد ، فقط تا پایان اجرای کدهای آن بلاک اعتبار دارد (تا پایان end) و در حافظه ی اصلی میماند و بعد از اتمام آن بلاک حذف میشود و اگر بلاکی تعریف نکنیم ، تا پایان اجرای آن رویداد باقی میماند
در واقع متغییر محلی 2 کاربرد اساسی دارد . اول برای مدیریت جزئی حافظه ی رم و دوم برای اینکه اگر شک داریم و نمیدانیم اسمی از متغییری را که به عنوان نام متغییر محلی که داریم مینویسیم ، جای دیگری بکار بردیم یا نه و کلا نگران اوررایت شدن آن متغییریم ، از متغییر محلی استفاده میکنیم
برای درک بهتر ، میتوانید این مثال را ببینید که در هر بلاک ، متغییر محلی ، فقط در همان بلاک اعتبار دارد (گویی برای هر متغییر محلی تا پایان آن بلاک ، فضای جداگانه در حافظه اختصاص داده میشود ، صرف نظر از متغییری با همان نام که قبلا ایجاد شده بود یا نه (و آیا این متغییر قبلا از نوع محلی بود یا سراسری) و بعد از اتمام آن پاک میشود) :



do
local loc=5
do
local loc="x"
do
local loc=1589
Dialog.Message("Notice", loc, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
Dialog.Message("Notice", loc, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
Dialog.Message("Notice", loc, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
Dialog.Message("Notice", type(loc), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



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



do
local funct
function funct(a)
a=a+1
return a
end
end


Dialog.Message("Notice", funct(1), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


و



do
local t={5,98}
end


Dialog.Message("Notice", t[2], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



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



function examp()
Dialog.Message("Notice", type(loc), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


do
local loc=4
examp()
end



که در تابع examp ، متغییر loc که در بلاکی دیگر ایجاد شد اما از همان بلاک ، تابعی فراخوانی شده است که داخل آن تابع ، متغییر محلی بلاک دیگر فراخوانی شده است ، مقدارش nil است


با تشکر از تمام کسانی که در این آموزش سهیم بودند

SajjadKhati
16-12-15, 10:11
یک نکته کوچک درباره تابع :

ایجاد و فراخوانی آرایه به روشی دیگر :
اگر آرایه ای داشته باشیم که key (یا به اصطلاح سمت چپ تساوی آن ، در متغییری ذخیره شده باشد) ، مثل متغییر foo و x و y در آرایه ی زیر باشد:



t={foo="bar",x=5,y=true}


به 2 حالت میشود آنرا فراخوانی کرد که اول به این گونه:



t.foo
t.x
t.y


برابر است با نوع دوم اش به این گونه :



t["foo"]
t["x"]
t["y"]


برای ایجاد آرایه هم میشود key ها را بصورت در هم شماره بندی و مقدار دهی کرد یعنی :



t={[3]=12,[7]="salam",[1]="word",[10]=653}
Dialog.Message("Notice", t[1].."\n"..t[3].."\n"..t[7].."\n"..t[10], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


که [1]t برابر رشته ی "word" و [3]t برابر 12 و [7]t برابر رشته ی "salam" و [10]t برابر 653 هست . توجه کنیم که در این حالت بقیه ی مقادیر nil هست مثل [2]t و ... و هم اینکه نباید در این حالت که همه ی key ها شماره گذاری شده اند ، بعضی از آنها را شماره گذاری نکنیم مثلا این طور صحیح نیست :



t={[3]=12,"salam",[1]="word",653}

SajjadKhati
18-01-16, 01:26
جا داره یه تشکر کامل از دوستم استاد اشکان کنم که این آموزش برگرفته از این آموزش آقا اشکان هست از پرشین کدرز هست (Only the registered members can see the link)

فرض کنید یک تابعی نوشتیم بنام new . میدونیم که برای اجرا باید علامت تابع که پرانتز هست رو باید بزاریم . یعنی اگه بنویسیم



new()


تابع اجرا میشه و اگه مقدار بازگشتی ای داشته باشه ، برگردونده میشه (مثلا اگه عدد رو برگردونه ، نوع بازگشتی اش عددی میشه) که تا اینجا همه مون میدونیم ولی اگه این پرانتز رو نزاریم ، یعنی هر جا نام فقط new بدون پرانتز رو ببریم ، مشخصه که نوع بازگشتی اش کل تابع (منظور مقدار بازگشتی این تابع که برای مثال عدد بود ، نمیشه دیگه) هست



function new()
return 5
end


Dialog.Message("Notice", type(new()), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


و



function new()
return 5
end


Dialog.Message("Notice", type(new), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


حالا اگه وقتی طبق تابع new در بالا (که عدد رو برمیگردون) متغییری رو برابرش بگیریم :



func = new()


مشخصه که چون تابع new اجرا شد پس مقدار بازگشتی اش که عدد هست رو برمیگردونه یعنی new() برابر عدد میشه پس متغییر func هم همون نوع عددی هست ولی توی کد زیر :



func = new


چون تابع اجرا نشد ، پس new کل تابع رو برمیگردونه یعنی هم new و هم متغییر func از نوع تابع یا function هستن
یک کاربرد این قضیه برای زمانی هست که بخوایم عملکرد یک تابع رو تغییر بدیم . مثلا اگه ماژول یا همون تابع Dialog.Message و هر تابع دیگه ای رو که خیلی توی پروژه استفاده کرده باشید و اگه بخاین تغییری توی کل این تابع بدین (مثلا متن نوار عنوان شو عوض کنین یا یه چیزی بهش اضافه کنین و هر تغییر دیگه ای) باید کل و به تعداد همون توابعی که توی پروژه استفده کردین ، تک تک و دونه دونه باید آرگومان ها رو تغییر بدین که کار بسیار دشواری هست .
با این روش میتونین عملکرد تابع (که قبلا نوشته شده . حالا چه خودمون نوشتیم یا در AMS نوشته شده و حتی توابع لوا) رو تغییر بدین .
اولا که چون برای ویرایش تابع قبلی ، هم به این نیاز داریم که یه تابع دیگه هم نام همون تابع ایجاد کنیم و هم به اینکه در بدنه ی این تابعی که همنام با توابع تابع مورد نظرمون هست ، نام همون تابع رو فراخونی کنیم و از طرفی هم فراخونی یک تابع به این روش باعث ارور میشه لذا باید قبل از همه چیز برای فراخونی تابع مورد نظر ، ازش در یک متغییر دیگر کپی (بکاپ) گرفت یعنی توی یه متغییر دیگه ریخت به همون روش بالا و متغییر پشتیبان گرفته شده رو توی بدنه ی تابعی که میخوایم ویرایش کنیم ، فراخونی کنیم :

این کد رو توی Global Function بزارید :



function new()
return 5
end


و اینو توی رویداد On Click یه دکمه :



func = new
Dialog.Message("Notice", type(new), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


old_new=new
function new()
return old_new().." salam"
end


Dialog.Message("Notice", new(), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);

SajjadKhati
11-03-16, 11:55
Pattern ها در لوا :


Only the registered members can see the link


پترن ها ، رشته هایی هستند که در برخی از آرگومان توابع مربوط به رشته یا string در لوا بکار میروند و برای پیدا کردن کاراکتر یا مجموعه ای از کاراکترهای خاص در یک رشته ، کاربرد دارند مثلا اگر بخواهیم فقط کاراکترهای مربوط به عدد را در رشته ای که ترکیب کاراکتر عدد و حروف و کاراکترهای سجاوندی دارند ، بیابیم به راحتی با استفاده از پترن ها ، انجام شدنی است . یک بخش بسیار مهم برنامه نویسی ، قابلیت کاربر در دستکاری و بازی و قابلیت انعطاف پذیری در رشته هاست. در این میان ، پترن ها ، نقش بسیار مهم و تاثیر گذاری را برای برنامه نویس ایجاد میکنند تا بتواند بسیار منعطف تر با رشته ها کار کند بنابراین پترن ها نه تنها در کار با رشته ها تاثیر خیلی مهمی دارند ، بلکه حتی میتواند در قالب کلی و نتیجه ی یک نرم افزاری که برنامه نویس مینویسد ، بسیار تاثیر گذار باشد
اینکه پترن ها را چگونه بکار میروند و قضیه اش چیست را در ادامه بررسی میکنیم اما در ابتدای کار باید بدانیم که پترن ها را کجا و در کدام توابع میتوانیم بکار ببریم؟
در هر آرگومانی از توابع string که در توضیح آن (در سایت اصلی لوا) عبارت pattern آمده باشد ، میتوانیم از پترن ها استفاده کنیم . مثلا در آرگومان دوم تابع string.find و string.gmatch و string.gsub و string.match
توابع بالا ، کاربردشان به ترتیب ، string.find برای پیدا کردن یک رشته (یا پترن) در رشته ی مورد نظر هست و بسیار بسیار مهم است . هر چند 3 تابع دیگر قابلیت کار با پترن ها را دارند ولی عملکرد این تابع (که برا پیدا کردن رشته هست) باعث میشود که این تابع قابل درک و ساده تر باشد مثال هایش و اکثر تمرین های این آموزش ، با مثال از این تابع هست و پترن در توابع دیگر ، مثل این تابع هستند . تابع string.gmatch ، تابع iterator ای است که در هر بار فراخوانی ، پترن مورد نظر را در رشته ی مورد نظر جستجو میکند (شبیه عملکرد تابع find را دارد) . تابع string.gsub هم در رشته ای ، رشته یا پترن مورد نظر را جایگزین میکند
علاوه بر اینها ، با توابع string.char که کد اسکی (عدد) مورد نظر را میگیرد و کاراکتر آنرا برمیگرداند و تابع string.byte را که دقیق برعکس تابع string.char عمل میکند و تابع string.sub هم که تکه ی مورد نظر از رشته را برش میدهد و برمیگرداند هم اندکی کار میکنیم (تابع string.sub که رشته را برش میدهد با تابع string.gsub که رشته یا پترن را جایگزین نمونه ی آن در رشته میکند ، اشتباه گرفته نشود)

بگذارید ابتدا آرگومان و خروجی توابع string.find را بررسی کنیم (همانطور که میدانیم ، این تابع برای جستجوی یک پترن یا رشته داخل رشته ی مورد نظر هست) :
آرگومان اول این تابع ، رشته ی مورد نظر که میخواهیم پترن را در آن جستجو کنیم ، قرار میدهیم
در دومین آرگومان ، پترن یا رشته ای را که میخواهیم در رشته ای که در آرگومان اول دادیم ، جستجو شود را مینویسیم
در سومین آرگومان که اختیاری هست ، عددی را وارد میکنیم مبنی بر اینکه در رشته ی مورد نظرمان (که در آرگومان اول وارد کردیم) از چندمین کاراکتر ، شروع به جستجو کردن کند (بصورت پیش فرض ، از کاراکتر اول از رشته شروع به جستجو میکند)
آرگومان چهارم هم اختیاری و بولین هست و پیشنهاد میشود که مقدار این آرگومان داده نشود
مقدار خروجی این تابع ، در صورتی که رشته ی جستجو شده ، پیدا نشود ، مقدار nil برگردانده میشود در همه ی متغییرهای خروجی این تابع . اگر مقداری پیدا شود ، در متغییر اول آن ، عدد اولین کاراکتری که پیدا شد و در متغییر دوم آن ، عدد آخرین کاراکتری که پیدا شد ، ذخیره میشود . در صورتی که در پترن (دومین آرگومان این تابع) ، از capure ها (که علامت پرانتز دارند و بعدا آشنا میشویم) استفاده شود ، خود آن رشته ای که پیدا شد ، به ترتیب در متغییرهای متوالی ذخیره میشود یعنی اولین capture (رشته ی داخل پرانتز) در سومین متغییر این تابع و دومین capture (رشته ی داخل پرانتز) در چهارمین متغییر این تابع و غیره ... ذخیره میشوند








Only the registered members can see the link a9cb30159750713.png (Only the registered members can see the link 30a781a8c.png)
تابع string.sub (که رشته ای را برش میدهد) :
در اولین آرگومان ، رشته ای را که میخواهیم برش دهیم را وارد میکنیم
در دومین آرگومان ، عدد کاراکتری که میخواهیم شروع برش از آنجا شروع شود
و در آخرین آرگومان ، عدد کاراکتری که پایان برش به آنجا ختم میشود
به عنوان خروجی ، اگر با خطا مواجه بشویم ، nil وگرنه رشته ی برش داده شده را برمیگرداند


مثال ها:
1)



Str = "padvish eps 1.7"
OneChar, LastChar= string.find(Str, "padvish")
CutedStr = string.sub(Str, OneChar,LastChar)
Dialog.Message("Notice", "First Charracter : "..OneChar.."\nLast Charracter : "..LastChar.."\nCutted Charracters : "..CutedStr , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


مشخص است که در خط دوم ، تابع string.find ، رشته ای را که در آرگومان دوم یعنی "padvish" را در رشته ای که در متغییر Str ذخیره شده ، جستجو و در صورت پیدا کردن ، شماره ی اولین کاراکتر پیدا شده را در متغییر اولی که OneChar هست ذخیره میکند (که در مثال بالا برابر 1 میشود چون رشته ی "padvish" در رشته ی مورد نظر ، از کاراکتر اول شروع شده و تا کاراکتر هفتم ادامه میابد) بنابراین در متغییر LastChar ، شماره ی مربوط به آخرین کاراکتر پیدا شده که 7 میباشد ، ذخیره میشود (دقت کنید اگر تابع string.find( چیزی پیدا نکند ، در اولین متغییر آن یعنی OneChar در مثال بالا ، nil مقداردهی میشود)
متغییر string.sub هم که شماره ی کاراکتر داده شده (کاراکتر آغاز که متغییر OneChar و پایانی که LastChar هست) در آرگومان 2 و 3 ی این تابع را از رشته ی مورد نظر که در آرگومان اول داده میشود ، جدا میکند و در متغییر CutedStr میریزد پس در رشته ی Str ، اولین تا هفتمین کاراکتر که میشود رشته ی "padvish" ، جدا میشود و در متغییر CutedStr به عنوان رشته ذخیره میشود
اما قرض از طرح این سئوال ساده ، نکته ی بسیار ساده و در وهله ی اول بسیار ابتدایی ست اما بسیار اساسی برای درک مفاهیم بعدی . آن هم اینکه در آرگومان دوم تابع string.find که رشته یا پترن مورد نظر (پترن که همان از نوع رشته ای هست) را برای جستجو (در رشته ی آرگومان اول) مینویسیم ، دقیقا کاراکتر به کاراکتری که مینویسیم ، اگر دقیقا و آن هم به ترتیب همان کاراکترها (حتی حساس به حروف کوچک و بزرگ) را در رشته ی آرگومان اول پیدا کرد ، نتیجه حاصل میشود یعنی در مثال بالا ، اگر تابع string.find ، در رشته ی Str ، به ترتیب حروف p و بعد از آن a و بعد از آن d و بعد از آن v و بعد از آن i و بعد از آن s و بعد از آن h را آنهم با همه ی حروف کوچک یافت ، پس تابع string.find نتیجه ای را حاصل میکند و مقداری را بازمیگرداند
یعنی الان اگر در آرگومان دوم این تابع ، بنویسیم "padvish eps" حاصل ایجاد میشود (مقدار OneChar برابر 1 و مقدار LastChar برابر 11 میشود) ولی اگر کوچکترین دستبردی در آن ببریم ؛ مثلا یکی از کاراکترها را با حروف بزرگ در آرگومان دوم بنویسیم یعنی مثلا بنویسیم "padvish Eps" چون در رشته ی Str دقیق مثل آن پیدا نشد یعنی حرف "E" با حرف بزرگ نوشته نشد ، مقداری را این تابع برنمیگرداند و نتیجه حاصل نمیشود یا مثلا اگر بجای یک فضای خالی بین padvish و eps از دو فضای خالی استفاده کنیم یا کمتر کنیم و یا هر کاراکتر دیگری و در کل هر دستکاری ای بجز دقیق خود همان متن کنیم تنها در صورتی نتیجه حاصل میشود که دقیق و عدل کپی همان چیزی که نوشته شد ، باشد ، مقدار حاصل میشود اما چون این گونه نیست (مثلا استفاده از دو فضای خالی بین padvish و eps بجای یکی) پس نتیجه ای حاصل نمیشود و مقدار اولین متغییرش nil میشود



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

. ----> کاراکتر نقطه (دات) هست و همه ی کاراکترها را برمیگرداند
a% ----> کاراکترهای تحت عنوان letter (همان حروف بدون هیچ کاراکتر سجاوندی یا space یا عددو ...) را برمیگرداند
c% ----> کاراکترهای کنترلی را برمیگرداند
d% ----> کاراکترهای عدد (digit) را برمیگرداند
l% ----> کاراکترهایی که حروف کوچک هستند (lower) را برمیگرداند
p% ----> کاراکترهای سجاوندی یا punctuation (مثل علامت ها مثلا .×÷@-_ و... البته بدون space) را برمیگرداند
s% ----> کاراکتر فضای الی یا space را برمیگرداند
u% ----> کاراکترهایی که حروف بزرگ هستند (upper) را برمیگرداند
w% ----> کاراکتر الفبایی (شامل حروف و عدد) را برمیگرداند
x% ----> عددهای شانزده دهی را برمیگرداند

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



2)



MainStr = "this year is 2015 (christian)"
i, j = string.find(MainStr, "%d")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


خوب دقت کنید که از این به بعد به بحث های اصلی وارد میشویم
همانطور که گفته شد ، پترن ها رشته هستند پس در خط سوم تابع string.find در آرگومان دوم آن ، باید پترن d% را در یک رشته بیاوریم و این گونه بنویسیم "d%"
از این به بعد اگر من در توضیحاتم ، چه داخل رشته نوشتم و چه برای تسریع در توضیحات ، داخل علامت رشته نگذاشتم ، درست آن در قسمت کدهاست و داخل علامت رشته
"d%" در رشته ی MainStr جستجو میکند و هر گاه در آن عددی پیدا کرد ، فقط اولین کاراکتر آن عدد را برمیگرداند یعنی در مثال بالا ، در خط سوم و در متغییر CutedStr ، فقط عدد 2 (که اولین کاراکتر عدد 2015 در رشته ی اصلی هست) ذخیره میشود نه اینکه کل عدد 2015 ذخیره شود

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



3)



MainStr = "this year is 2015 (christian)"
i, j = string.find(MainStr, "is%s%d")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


دقت کنید که در این آموزش ، اغلب مثال ها بسیار شبیه هم هستند اما یا پترن ها یا رشته ی اصلی آن ها متفاوت هست
تفاوت مثال شماره ی 3 و 2 ، در قسمت پترن آن است (از این به بعد تفاوت ها گفته نمیشو.د برای اتلاف وقت)
در این پترن آمده "is%s%d"
یعنی هرگاه ابتدا کاراکتری بنام i و حتما بعد از آن کاراکتر s و حتما بعد از آن s% که همان فضای خالی میشود (فقط یک کاراکتر از فضای خالی آمد) و بعد از آن هم d% در رشته ی اصلی آمد (فقط یک کاراکتر از عدد را میگیرد)، آنرا پیدا کن که در رشته ی اصلی هم اینرا میبینیم که کلمه ی is آمد و بعد از آن فضای خالی آمد و بعد از آن هم عدد آمد پس در متغییر CutedStr رشته ی "is 2" ذخیره میشود
پس آنهایی که معنای خاص دارند ، اغلب بعد از علامت % هستند (علائم دیگر بعدا گفته میشود ولی مهمترین همین هست) و بعد و قبل آن هر کاراکتری که آمد (مثلا در پترنو آرگومان دوم تابع string.find که کاراکتر is قبل از s% آمد و یا حتی اگر بعد از آن میامد) به عنوان کاراکتر معمولی در رشته ی اصلی یافت میشوند و معنای خاصی ندارند مثل مثال اول
** در مثال شماره ی 3 اگر در پترن ، عبارت s% را نمیگذاشتیم (یعنی این گونه مینوشتیم "is%d" ) به این معناست که بعد از کاراکتر i و s ، دقیقا بعد آن و بدون هیچ کاراکتر دیگری ، کاراکتری از نوع عدد بیاید و چون بعد از کاراکتر s ، کاراکتر عددی نیامد و کاراکتر space یا فضای خالی آمد ، پس نتیجه ای حاصل نمیشود و مقدار nil در متغییر i ذخیره میشود که باید خوب به این موضوع دقت کنیم
** در مثال بالا کلا بجای هر کاراکتری ، کاراکتری بیاوریم که ترتیب پترن ها را بهم بریزد ، نتیجه ای حاصل نمیشود یعنی اگر بجای d% یک کاراکتر غیر عدد بیاوریم یا بجای s% یک کاراکتر غیر از فضای خالی بیاوریم یا حتی جابجایی در این کاراکترها در رشته ی اصلی انجام بدهیم یا کاراکتر is در رشته ی اصلی قبل از این کاراکترها نیاید



علامت ها در پترن ها:
به این علامت ها magic کاراکتر هم گفته میشود و در عین سادگی ، بسیار نقش مهمی در پترن ها دارند

+ ---> علامت مثبت هست که کاربردش این هست که اگر بعد از پترن یا کلاس کاراکتری (منظور همان کلاس کاراکترهایی که در بالا گفته شد مثل d% و ... ) بیاید (قبل از آن نه) بجای اینکه کاراکتر را دانه دانه برگرداند ، کل آن نوع کاراکتر را از اولین کاراکتری از همان نوع که شروع شد و تا آخرین کاراکتری که از همان نوع ختم میشود را برمیگرداند و علامت بسیار کاربردی هست
علامتهای + و * و ? هم هستند که ابتدا بگذارید تقریبی با علامت + که اندکی آشنا شدیم ، بعد به این علامت ها میپردازیم



4)



MainStr = "this year is 2015 (christian)"
i, j = string.find(MainStr, "is%s%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تفاوتش با مثال 3 فقط در آرگومان دوم خط 2 هست که آخر آن و بعد از کلاس کاراکتر d% علامت + آمد و این به این معناست که بجای اینکه فقط تک تک این کاراکترها را هر بار جداگانه پیدا کنی (که باید در این صورت در حلقه ای آرایه های آنرا به هم پیوند دهیم و کار دشواریست) ، هر گاه بعد از space کاراکتر ، کاراکتر عددی را پیدا کردی ، از ابتدای آن کاراکتر عددی (که 2 هست) تا انتهای آن کاراکتر عددی (که 5 هست) را یکجا برگردان. پس نتیجه میشود "is 2015"
دقت کنید که چون بعد از آخرین کاراکتر عددی در رشته ی اصلی که 5 (آخرین عدد 2015) هست ، کاراکتری از نوع دیگر که کاراکتر space هست آمد ، پس حتی اگر بعد از آن کاراکتر space (که بعد از 5 آمد) ، کاراکترهای عددی دیگری میآمد دیگر +d% باز هم فقط کاراکتر ععدی 2015 را برمیگرداند چون انتهای مجموع کاراکتر عددی آن کاراکتر 5 (از عدد 2015) بود و بعد از آن نوعش غیر از عددی میشد (حتی یک کاراکتر) یا مثلا اگر بجای space هر کاراکتر غیر از عددی مثلا نقطه میامد و بعد از آن دوباره عدد میامد ، باز هم هم همین یا کاراکتر حرفی یا سجاوندی و ... میامد ، باز هم همین

در این مثال ، اگر در رشته ی اصلی ، بین is و 2015 ، بجای یک کاراکتر space ، از چند کاراکتر space استفاده کنیم ، حاصلی پیدا نمیشود چون s% موجود در پترن ، چون تنها آمد پس فقط در صورتی که یک کاراکتر space بین کاراکتر is و 2015 قرار گیرد ، نتیجه یافت میشود چون بعد از s% علامت + نگذاشتیم که از ابتدا تا انتهای کاراکتر space را شامل شود یعنی هر علامت + فقط به کلاس کاراکتر قبل از خود تعلق دارد



5)



MainStr = "this year is 2015 (christian)"
i, j = string.find(MainStr, "is%s+%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تفاوت آن فقط در علامت + قبل از s% در آرگومان دوم خط 2 و space های بیشتر در رشته ی اصلی هست که در توضیحات آخر مثال 4 گفته شد بنابراین هر چقد کاراکتر space اگر بین کاراکترهای is و عدد (2015) بگذاریم (نه کاراکتری از نوع دیگر بین شان بذاریم) کل آن فضای خالی ، به حساب میاید (از ابتدای فضای خالی تا انتهای آن) اما اگر در مثال شماره ی 4 حتی یک کاراکتر فضای خالی ، اضافی تر میگذاشتیم در رشته ی اصلی ، نتیجه ای یافت نمیشد و nil برگردانده میشد
در مثال بالا ، "is 2015" برگردانده میشود



*** تذکر مهم : اگر در پترن ها ، بعد از علامت % کاراکتری بیاوریم بجز کلاس کاراکترها (کلاس کاراکترها همان a% و w% و d% و ... که در بالا گفته شد) ، کاراکترِ بعد از کاراکتر % را در رشته ی اصلی جستجو میکند مثلا اگر در پترن ها بنویسیم i% چون این عبارت در کلاس کاراکترها موجود نیست ، در رشته ی اصلی ، کاراکتر i را جستجو میکند و هیچ فرقی با اینکه در پترن کاراکتر i را بنویسیم برای جستجو ، با عبارت i% ندارد ولی کاربرد این % زمانی است که بخواهیم یک عبارتی که در پترن ها معنای خاصی دارد و در رشته ی اصلی آمده را جستجو کنیم مثلا کاراکتر + یا - یا ? و ... را که در پترن ها معنای خاصی دارند ولی در رشته ی اصلی بخواهیم این کاراکترها را جستجو کنیم ، در پترن قبل از این کاراکترها ، کاراکتر % را مینویسیم یعنی در پترن مینویسیم +% یعنی کلمه ی بعد از % (اگر در کلاس کاراکتر موجود نباشد) را که علامت + هست ، در رشته ی اصلی جستجو کن
البته بجز کاراکتر بک اسلش \ و علامت گیومه " که در پترن ها ، قبل از هر کدام ، یک علامت بک اسلش دیگر میگذاریم یعنی اگر در رشته ی اصلی علامت " وجود داشت ، در پترن ها برای جستجوی آن ، قبل از آن مینویسیم "\ و برای علامت \ هم در رشته ی اصلی و هم در پترن ، در هر دو جا باید از دو علامت بک اسلش استفاده کرد یعنی \\ بنویسیم (بجز این و کلاس کاراکترها ، برای کاراکترها و علامت های معنا دار در پترن ها ، قبل از آن کاراکتر از % استفاده میکنیم برای جستجو)
کاراکتررها و علامت های معنا دار در پترن ها هم عبارتند از :

( ) . % + - * ? [ ^ $

که آرام آرام با آنها آشنا میشویم



6)



MainStr = "this year is+ 2015 (christian)"
i, j = string.find(MainStr, "is%+%s%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنید
نکته ی مهم اینجا در پترن ، عبارت +% هست . همانطور که در نکته ی بالا گفته شد ، اگر بعد از % کلاس کاراکترها نیایند (مثل s% و a% و ...) و بعد از آن علامتِ % هر کاراکتری که بیاید ، همان کاراکترِ بعد از % در رشته ی اصلی جستجو میشود که برای کاراکترهای معنادار در پترن ها برای جستجو کاربرد دارد البته بجز علامت \ و علامت گیومه "
در پترن رشته ی



"is%+%s%d+"


آمده . یعنی اگر کاراکتر i آمد و بعد از آن کاراکتر s آمد و بعد از آن کاراکتر + آمد (چون قبل از علامت + کاراکتر % آمد پس علامت بعد از % اگر جزء کلاس کاراکترها نباشند که نیست پس علامت بعد از آن که + هست در رشته ی اصلی جستجو میشود) و بعد از آن فقط یک کاراکتر space آمد (چون بعد از s% علامت + نیامد که به معنای مجموع کاراکتر space باشد ، پس اگر بیشتر از یک کاراکتر space بیاید ، قابل قبول نیست) و بعد از آن هم عدد آمد ، مجموع آن عدد (از اول عدد تا آخرین کاراکتر از نوع عدد) ، مقدار آنرا برگردان پس مقدار "is+ 2015" برگردانده میشود



7)



MainStr = "this year is+ ++++++++++++++++++++2015 (christian)"
i, j = string.find(MainStr, "is%+%s+%++%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنید
پترن



"is%+%s+%++%d+"


اگر کاراکتر i و بعد از آن کاراکتر s و بعد از آن +% یعنی تنها یک کاراکتر + و بعد از آن مجموع کاراکتر space (چون بعد از s% علامت + که معنای مجموع آن نوع کاراکتر را دارد ، آمد) و بعد از آن ++% که اولین علامت + بعد از % یعنی اینکه این علامت بعد از کاراکتر space آمده باشد و علامت + بعدی آن به این معناست که مجموع کاراکترهای + که بعد از کاراکتر space که هر چقدر آمد (که تعداد شان بعد از کاراکتر space برابر 20 تاست) و بعد از کاراکتر + هم مجموع کاراکتر عددی را حساب کن پس متغییر CutedStr مقدار "is+ ++++++++++++++++++++2015" را برمیگرداند



8)



MainStr = "this year is+ 2015 (christian)"
i, j = string.find(MainStr, "is+%s%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنید
این مثال ، یک مثال اشتباست به این دلیل که در پترن بعد از i و s کاراکتر + آمد و چون عبارت قبل از آن از نوع کلاس کاراکتر نیست (یعنی s% یا a% و ... نیست) و این علامت هم در پترن معنای خاصی دارد ، پس باید قبل از آن علامت % میآمد که نیامد پس در متغییر i مقدار nil برگردانده میشود و ارور میدهد



9)



MainStr = "this year is +2015 (christian)"
i, j = string.find(MainStr, "is%s++%d+")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنید
** هر چند این مثال درست هست و مقداری را برمیگرداند و شبیه مثال قبلی هست اما اصولی نیست و این جور نوشتن در پترن ها پیشنهاد نمیشود اما چرا؟
نکته اینجاست که در پترن عبارت ++s% آمد اولین علامت + بعد از s یعنی +s% به این معناست که بعد از کاراکتر i و s مجموع کاراکترهای space آمد و دومین علامت + بعد از s یعنی ++s% یعنی فقط یک کاراکتر + بعد از آن آمد و بعداش هم که مجموع اعداد (از اولین کاراکتر عدد تا آخرین) آمد را جستجو کن پس رشته ی "is +2015" برگردانده میشود
پس کاراکترهای معنادار در پترن ها (در اینجا کاراکتر +) حتی اگر قبل از آن علامت % هم نیاید ، بسته به مکان ای که نوشته شد ، در رشته ی اصلی ، همان کاراکتر ممکن است جستجو شوند اما احتمال اشتباه برای این جور نوشتن پترن ها بسیار بالا میرود و اصولی نیست و بهتر است اگر کاراکترهای معنادار در پترن را میخواهیم جستجو کنیم ، قبل از آن از کاراکتر % استفاده کنیم (روش اصولی) . یک نمونه استفاده از این پترن ها را دیدیم که در مثال شماره 8 ارور داد پس بهتر است از روش اصولی آن استفاده کنیم



10)



MainStr = "this year is 2015 (christian)"
i, j = string.find(MainStr, "is %d%s+%(")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


این مثال هم اشتباست. بررسی میکنیم در پترن :
بعد از کاراکترهای i و s و بعد از آن یک کاراکتر space (دقت کنید که این بار بجای استفاده از پترن s% از خود فضای خالی در سومین کاراکترِ آرگومان دوم خط 2 استفاده شده که اگر فضای خالی استفاده شده در پترن دیقق برابر با تعداد فضای خالی موجود در رشته ی اصلی باشد ، چون کاراکترها دقیق یکسان هستند ، درست هست و در این مثال هم چون یک فضای خالی بکار برده شد ، پس در سومین کاراکتر ، یک فضای خالی گذاشتیم که درست هست . البته مشخص هست که بجای این مثل موارد قبل از s% هم میشد استفاده کرد) و تا اینجا مشکلی وجود ندارد و بعد از کاراکتر space چون d% تنها آمد و بعد از آن علامت مجموع کاراکتر هم نوع (عدد) که همان علامت + هست ، نیامد پس فقط اولین کاراکتر عدد که 2 هست را برمیگرداند (نه کل مجموع کاراکترهای عدد را که 2015 هست) و بعد از آن (یعنی بعد از 2 که تا اینجا برگرداند شد یعنی تا اینجا رشته ی "is 2" برگردانده شد) هم +s% آمد یعنی بعد از عدد 2 ، فضای خالی باشد ولی چون در رشته ی اصلی بعد از 2 باز هم کاراکتر عددی است (عدد 0 هست) نه کاراکتر space پس نتیجه ای حاصل نمیشود و مقدار nil در متغییر i برگردانده میشود
اما در همین مثال اگر بعد از d علامت + را بکار ببریم ، یعنی بنویسیم +d% چون تا آخر عدد که 5 هست را برمیگرداند و بعد از آن هم چون در پترن عبارت +s% را آوردیم و در رشته ی اصلی بعد از این عدد 5 ، فضای خالی هست (چون +s% آوردیم پس یک کاراکتر فضای خالی یا 1000 کاراکتر فضای خالی پی در پی یا بیشتر هم فرقی نمیکند باشد یا نه) و بعد از آن هم عبارت ")%" در پترن بکار برده شد چون علامت پرانتز عبارت معنا داری در پترن ها هست (که در ادامه با این علامت پرانتز آشنا میشویم) و میخواهیم آنرا در رشته ی اصلی جستجو کنیم ، قبل از آن علامت % را گذاشته شد و چون بعد از space علامت پرانتز بسته آمده ، پس مقدار رشته ی



"is 2015 ("


در متغییر CutedStr ذخیره میشود



11)



MainStr = "this year is 2015 (christian / 1394\\ solar)"
i, j = string.find(MainStr, "is %d+%s+%(%a+%s/%s%d+\\")
CutedStr = string.sub (MainStr, i , j)
Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



دقیق مثل مثال بالاست فقط با کاراکترهای اضافه تر
در قسمت پترن این ها اضافه شد پس فقط این قسمت توضیح داده میشود :



"%a+%s/%s%d+"


یعنی بعد از کاراکتر پرانتز بسته که در مثال بالا کار شد ، مجموع کاراکترهای حروفی که همان +a% هست و در اینجا همان christian میشود و بعد از آن هم یک فضای space و بعد از آن هم یک کاراکتر اسلش (نه کاراکتر بک اسلش که این شکلی \ بود بلکه کاراکتر اسلش که این شکلی / هست و موردی ندارد برای جستجو) و بعد از آن هم فقط یک کاراکتر space و بعد هم مجموع کاراکتر عددی و بعد از آن هم کاراکتر بک اسلش \ بود (دقت کنید همانطور که گفته شد ، برای استفاده از کاراکتر بک اسلش یعنی \ هم در رشته ی اصلی که متغییر MainStr هست و هم در پترن که آرگومان دوم تابع خط دوم هست ، حتما در هر 2 جا باید دو تا علامت بک اسلش بیاید) که در کل این مثال رشته ی "\is 2015 (christian / 1394" را برمیگرداند
دقت کنید اگر علامت پلاس را همانطور که در مثال قبل توضیح داده شد ، برای کلاس کاراکترها نگذاریم بسته به تعدادشان امکان ارور هست یعنی در این مثال برای اولین d% و دومین که a% و آخرین که باز هم d% هست ، علامت + نگذاریم ، چون همانطور که در مثال قبل گفته شد ، کاراکترها ترتیب شان بهم میخورد پس ارور دریافت میکنیم و نتیجه ای حاصل نمیشود



* پس تا بحال باید این تجربه را کسب کرده باشید که در پترن ها اولا کاراکتر به کاراکتر معنا و مفهوم دارد و دوما با وجود پیچیدگی ها ، هر چند کوچک ترین اشتباهی و یا بهم زدن ترتیب آن باعث ارور میشود اما ساده هست و مهم ترین نکته ی آن این است که هر گاه علامت % آمد ، ببینیم بعد از آن آیا کاراکتر کلاس آمد یا نه و بعد از آن مه حواسمان به کاراکترهای magic در پترن ها باشد (+ و - و * و ?) و بجز اینها هم حواسمان به کاراکترهای معنا دار در پترن ها مثل پرانتز و کلوشه و ... که گفته شد باشد
* علامت های (Magic کاراکترهای) منفی - و ضرب * و علامت سئوال ? هم باشد بعد از توضیح برخی از کاراکترها و علامتهای دیگر با آنها آشنا میشویم

Capture ها :
همان علامت های پرانتز در پترن ها هستند و همانطور که در تصویر بالا میبینید ، وقتی در پترنی ، قسمتی از پترن ، داخل پرانتز قرار گیرد ، به ترتیب و بصورت متوالی ، قسمت هایی از پترن که داخل پرانتز هستند ، در متغییرهای مورد نظر (در اینجا متغییرهای تابع string.find) ذخیره میشوند . دو متغییر اول این تابع که همانطور که در مثال های بالا کار شد ، شماره ی کاراکترها را برمیگردانند. پس یعنی قسمتی از پترنی که داخل اولین پرانتز هست ، در سومین متغییر این تابع ذخیره میشود (اگر سومین متغییر را برای این تابع تعریف کنیم) و قسمتی از پترنی که داخل دومین پرانتز هست ، در چهارمین متغییر این تابع ذخیره میشود (اگر چهارمین متغییر را برای این تابع تعریف کنیم) و همین طور الی آخر (به اولین عکس دقت کنید)
این موضوع میتواند جایگزین تابع string.sub شود و به ما در کد نویسی کمتر بسیار کمک کند



12)



MainStr = "this year is 2015 (christian / 1394 solar)"
i, j, CutedStr1, CutedStr2 = string.find(MainStr, "(%a+)%s+(%d+)")
Dialog.Message("Notice", CutedStr1 .."\n"..CutedStr2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در پترن مورد نظر که



"(%a+)%s+(%d+)"


هست ، اول گمان میکنیم که پرانتزی وجود ندارد پس هر گاه +a% یعنی حروف (فقط حروف نه کاراکتری غیر از نوع حروف مثل space و عدد و سجاوندی و ...) بیاید و بعد از آن کاراکتر space بیاید و بعد از آن هم عدد ، نتیجه حاصل میشود (همانطور که آموختیم ، ترتیب مهم بود و البته چون علامت + دارند ، یعنی مجموع آن ها یعنی مجموع حروف و بعد از آن مجموع space و بعد هم مجموع عدد که اگر تکی هم بودند ، پذیرفته میشود)
از ابتدا چک میشود . هر چند در رشته ی اصلی ، عبارت this و یا year یک مجموع حروف هستند و اتفاقا بعد از آنها هر کدام space وجود دارند اما بعد از space از هر کدام از این حروف ها ، باز حروف دیگر وجود دارد و تنها حروفی که بعد از آن space و بعد عدد باشد رشته is هست پس در اینجا +a% همان رشته ی is و +s% همان space ها و +d% هم همان عددها که 2015 هست میشود و در کل در تابع رشته ی"is 2015" پیدا میشود اما مثل مثال های قبل ، نمایان و چاپ نکردیم در خط بعد
پس در متغییر i که اولین کاراکتر پیدا شده که همان کاراکتر i از is که شماره ی 11 هست ذخیره میشود و متغییر j که شماره ی آخرین کاراکتر که کاراکتر 5 از 2015 هست و شماره 17 را دارد ذخیره میشود (i=11 و j=17) . حالا در پترن ها ، هر قسمتی که داخل پرانتز نوشته شده بودند به ترتیب در متغییر مربوط به تابع string.find و بعد از دومین متغییرش (یعنی بعد از متغییر j که در این متغییر ، عدد ذخیره شد) مقادیر داخل پرانتز در پترن ها ، آنهم بصورت رشته ذخیره میشوند یعنی داخل اولین پرانتز در پترن ها که +a% هست ، و مقدارش در مثال بالا رشته ی "is" میشد ، در سومین متغییر تابع string.fing که CutedStr1 هست ، ذخیره میشود (در صورتی که این متغییر وجود داشته باشد) و قسمتی که داخل پرانتز نیست که همان +s% هست ، داخل هیچ متغییری ذخیره نمیشود اما وجودش برای یافتن و به اصطلاح match کردن در رشته ی اصلی تا اینکه طبق الگوی رشته و به ترتیب کاراکتر باشد ، بسیار ضروری است وگرنه نتیجه ای حاصل نمیشود و مقدار nil در اولین متغییر که i هست برگردانده میشود که در مثال های گوناگون کار کردیم و حاصل دومین قسمتی از پترن که داخل پرانتز هست یعنی +d% که مقدارش 2015 میشود در این مثال ، در متغییر بعد از آخرین متغییر (چهارمین متغییر که در اینجا همان CutedStr2 هست ذخیره میشود)
در خط آخر فقط رشته ی دلخواه خودمان را که خواستیم ، مقادیرشان را فراخوانی کردیم نه مقادیر کل پترن را یعنی پیام is و در خط پایین تر پیام 2015 را میدهد . دقت کنید چون آخر متغییرهای CutedStr1 و CutedStr2 عدد هست ، پس در خط آخر وقتی میخواهیم علامت در ادامه را که دو نقطه هست بنویسیم اگر موقع نوشتن و فراخوانی CutedStr1 در خط آخر و علامت دو نقطه ، بین شان یک فضای خالی نگذاریم ، زبان تصور میکند که در اولین نقطه ، برای عدد 1 که در آخر متغییر CutedStr هست ، اعشار گذاشتیم و نمیتواند متغییر را فراخوانی کند و ارور میدهد پس به space بین شان باید توجه داشت و یا آخر متغییر را عدد ندهیم (مثلا عدد را بین نام متغییر بدهیم یا اصلا ندهیم)
باز به این نکته اشاره میکنم که در این مثال ، فقط قسمتی از رشته ای را که پیدا کردیم را برگرداندیم نه کل آنرا آنهم در 2 متغییر جداگانه و قسمت وسط که فضای خالی بود را جزء خروجی در نمایش قرار ندادیم (هر چند در اینجا چون قسمتی که نمایش داده نشد ، فقط یک کاراکتر space بود چندان به چشم نمیاید ولی میشود بجای همین space هر نوع کاراکتر دیگر مانند عدد را در نظر نگرفت و هر چیزی را که دوست داریم ، حساب کنیم) هر چند میتوانیم کل عبارت یافته شده را با همین پرانتز در خروجی بیاوریم که مشخص و واضح است که باید کل پترن را در پرانتز قرار داد



13)



MainStr = "this year is 2015 (christian / 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "(%d+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


این حلقه ، رشته را تا آخر آن چک میکند
مثلا اگر در توابعی مانند قبل و بدون حلقه مینوشتیم ، یعنی مینوشتیم :



FirstChar, LastChar, Content1 = string.find(MainStr, "(%d+)")


البته در بدنه ای مثل بدنه ی مثال های قبل (یعنی بدون حلقه) ، فقط اولین مجموعه از آن کاراکتر را که در اینجا عدد هست را برمیگرداند یعنی فقط در رشته ی اصلی ، عدد 2015 را برمیگرداند و عدد 1394 که در کاراکترهای بعدی آمد را برنمیگرداند چون با اولین سرچ ، تمام جستجو را پایان میدهد اما در این مثال که حلقه موجود هست ، تابع string.find در سومین آرگومان خود ، میگوید که از کجا (کدام شماره ی کاراکتر) باید جستجو را شروع کند که متغییر LastChar ، این شماره ی کاراکتر را در اختیار آن قرار میدهد یعنی دفعه ی اول در اجرای حلقه (مقدار اولیه ی آن در خط 2 صفر هست) ، اولین کاراکتر را جستجو میکند تا پترن را در رشته ی اصلی پیدا کند و آخرین کاراکتری را که پیدا کرده ، باز هم در دومین متغییر همین تابع string.find که همان آخرین شماره ی کاراکتر پترن پیدا شده هست و به همین نام LastChar ذخیره میکند و در اجرای بعدی حلقه ، یک کاراکتر بعد از آخرین کاراکتری که پیدا کرد را دوباره جستجو میکند تا اینکه دیگر چیزی پیدا نکند و nil برگردانده شود (اگر جوابی حاصل نشود ، nil در هر 3 متغییر تابع string.find برگردانده میشود) . همانطور که مشخص هست ، باید دقت کرد که نام کاراکتری که مقداردهی اولیه برای جستجو کردیم ، با شماره ی آخرین کاراکتر پیدا شده باید یکی باشد بنابراین ، در دفعه ی اول که عدد مورد نظر (2015) پیدا شد ، در متغییر FirstChar ، شماره ی ابتدای کاراکتری که پیدا شد ، که 14 هست ذخیره و در متغییر LastChar شماره ی آخرین آخرین کاراکتر که 17 هست و در متغییر Content1 هم عدد 2015 (به عنوان رشته) ذخیره میشود و در دومین اجرای حلقه به همین ترتیب ، عدد 32 و 35 در متغییرهای FirstChar و LastChar و در متغییر Content1 هم عدد 1394 ذخیره میشود (در سومین اجرای حلقه چون از کاراکتر 36 به بعد ، عددی در رشته ی اصلی نیامده ، پس مقدار nil برگردانده میشود و از حلقه خارج میشود)



14)



MainStr = "this year is 2015 (christian / 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "(.)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


همانطور که در کلاس کاراکترها بیان شد ، علامت نقطه . به معنای هر نوع کاراکتری هست (همه نوع کاراکتر) و هر کلاس کاراکتری هم که بصورت تکی (یعنی بدون علامت + بعد از آن میآمد) به معنای برگردندن فقط یک کاراکتر مورد نظر بود و از آنجایی هم که این حلقه تا آخر برای جستجوی پترن مورد نظر (که در اینجا برای یافتن همه ی نوع کاراکترهاست) اسکن میکند ، پس به تعداد کاراکترها ، هر بار یک کاراکتر را برمیگرداند
اما در همین مثال اگر در پترن ، بعد از کلاس کاراکتر .% علامت + بگذاریم یعنی بنویسیم +.% مثل بقیه ی موارد به معنای مجموع همان نوع کلاس کاراکترهاست و چون کلاس کاراکتر .% همه ی انواع کاراکتر را اشاره میکند ، پس کل رشته ی مورد نظر ، برگردانده میشود
** دقت کنیم که اگر در رشته ی اصلی ای ، بخواهیم کاراکتر نقطه را جستجو کنیم ، چون این جزء کلاس کاراکترهاست و در پترن ها معنای خاصی دارد ، باید همانطور که توضیح داده شد ، قبل از نقطه ، علامت % را بگذاریم یعنی بنویسیم .%



15)



MainStr = "this year is 2015 (christian / 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "%d(.+)%s",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


در پترن مربوط به این مثال ، ابتدا برای d% دنبال عدد میگردد و عدد 2 (که همان ابتدای عدد 2015 هست) را مییابد و چون علامت + بعد از آن نیامد پس فقط منظور یک کارکتر و آنهم اولین کاراکتر عددی هست که پیدا میشود (که همان 2 هست) و بعد از آن +. آمد که یعنی بعد از عدد 2 ، همه ی کاراکترهای بعد از آن را انتخاب کن و بعد از آن هم s% آمد یعنی آخرین محتوای +. تا جایی ادامه یابد که قبل از آخرین کاراکتر فضای خالی باشد (که در اینجا آخرین کاراکتر فضای خالی ، بین عدد 1394 و حروف solar هست) بنابراین عبارت +. در پترن بعد از حرف 2 و قبل از آخرین فضای خالی هست و چون فقط این قسمت در پرانتز آمد پس فقط این قسمت در متغییر Content1 ذخیره میشود بنابراین مقدار آن برابر رشته ی



"015 (christian / 1394"


میشود
اگر دقت کنید و بخواهیم دقیق با ترتیب پیش برویم ابتدا که d% آمد یعنی عدد 2 (ابتدای عدد 2015) و بعد هم که +. آمد یعنی هر کاراکتر بعد از آن که از 0 شروع میشود و بعد از آن هم که s% آمد که اولین کاراکتر فضای خالی بعد از آن ، بعد از عدد 5 (بعد از آخرین عدد 2015) هست و طبیعتا مقدار +. یا در واقع مقدار متغییر Content1 برابر 015 میشد فقط اما چرا تا آخرین کاراکتر فضای خالی ، حساب کرد؟ چرا اولین یا حتی این همه کاراکتر فضای خالی بعد از عدد 2 را حساب نکرد؟
علامت یا magic کاراکتر + هر گاه بیاید ، در صورتی که چندین نمونه ی شبیه هم در رشته ی اصلی وجود داشته باشد ، همیشه از اولین تا آخرین نمونه (حداکثر تعداد کاراکتر) را به حساب میاورد ولی دقیق برعکس آن علامت منفی هست که کمترین نمونه را به حساب میاورد (البته این یکی از کارهای علامت - هست و کار مهم منفی را در آینده میآموزیم) یعنی اگر بجای علامت+. علامت -. در پترن بود ، مقدار بازگشتی ، کمترین و کوچک ترین نمونه که همانطور که بررسی کردیم ، کوچک ترین نمونه ی آن در وهله ی اول فقط عدد 015 هست ، برگردانده میشود و در اجرای بارهای بعدی حلقه ، باز هم چک میشود بعد از آن ، این نمونه در ادامه وجود دارد که نتیجه را برمیگرداند (در این مثال برای بار دوم عدد 394 را برمیگرداند) و یا نه



16)



s = [[then he said: "it's all right"!]]
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(s, "\"(.+)\"", LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


میخواهیم در رشته ی اصلی ، فقط کاراکترهای بین علامت دابل کوتیشن یا " " را استخراج کنیم و همانطور که گفته شد ، برای جستجوی علامت های \ و " در رشته ی اصلی ، در پترنِ آن ، قبل از این علائم ، یک بک اسلش یا \ قرار میدهیم پس در پترنِ این مثال "\ یعنی هر کجا کاراکتر " دیده شد و بعد از آن هم همه ی کاراکترها انتخاب شود (و بعد از آن هم که "\ آمد که باز هم یعنی) تا آخرین کاراکتر " که دیده شد و چون +. در پرانتز قرار گرفتند پس فقط مقادیر این قسمت که محتوای بین دابل وتیشن هستند در متغییر Content1 ذخیره میشود بنابراین محتوای این متغییر رشته ی it's all right میشود
در خط اول همانطور که میدانید میشود رشته را در دو علامت کلوشه ی باز و بسته [[ ]] تعریف کرد بجای تعریف در دابل کوتیشن " " و چون دابل کوتیشن در خط اول هم جزء رشته بود پس بهتر است داخل کلوشه تعریف شود (یا قبل از دابل کوتیشن ، علامت \ بگذاریم در رشته ی اصلی)





Capture ها (کلوشه) :
کپچرهای پرانتز را بررسی کردیم . حالا به کپچر کلوشه [ ] میپردازیم
تا به حال اگر دقت کرده باشید ، ترتیب پترن هایی که مینوشتیم بسیار بسیار مهم بود مثلا اگر مینوشتیم :



"(%d+%a-)"


در رشته ی اصلی حتما جایی که عدد (یا مجموع عدد) بیاید و دقیقا بدون هیچ کاراکتر اضافی دیگر ، کاراکتر بعدی آن حروف (یا مجموع حروف) بیاید تا این پترن جواب داشته باشد مثلا "hello 364word" که رشته ی 364word در اینجا پیدا میشود اما اگر رشته به شکل "hello 364 word" چون بعد از عدد ، کاراکتر space که از نوع دیگری است آمد و یا مثلا "hello word365"و یا هر ترتیب دیگر بیاید، دیگر این پترن جوابگو نیست
کپچرِ کلوشه ، زمانی استفاده میشود که ترتیب پترن ها برای ما مهم نباشد و هر کلاس کاراکتر و یا در واقع هر پترنی را که (داخل کلوشه بود) در هر جایی از رشته ی اصلی جستجو کرد ، در نتیجه و جواب میاورد و اگر آن پترن هم وجود نداشت ، اشکالی ندارد . مثلا اگه 3 کلاس کاراکتر (یا حتی کاراکتری) داخل کلوشه داشتیم ، در رشته ی اصلی ، بدون در نظر گرفتن ترتیب آن ، چه یکی از کلاس کاراکتر اول یا دوم یا سوم و یا حتی هر 3 تای آنها وجود داشته باشد ، نتیجه حاصل میشود و مقدار برگردانده میشود
بنابراین هر چند در کلوشه فقط یک کاراکتر یا کلاس کاراکتر را برای جستجو میشود نوشت ولی زمانی درک میشود که چندین کاراکتر یا کلاس کاراکتر وجود داشته باشد در کلوشه
*** بسیار دقت کنیم که magic کاراکترها یا همان علامت های + و - و * ? (مثلا علامت + که به معنای مجموع کارکتر همان نوع هست) را حتما حتما بعد از کلوشه بیاوریم وگرنه اگر داخل کلوشه بیاوریم ، به معنای جستجوی همان کاراکتر که قبلا مثلا با +% نشان میدادیم هست . کلاس کاراکتر نقطه هم دقیقا مثل همین است یعنی اگر داخل کلوشه بیاید فقط کاراکتر نقطه را جستجو میکند و معنایی که همه نوع کاراکتر را در کلاس کاراکتر ها را برمیگرداند را دیگر ندارد و مثل این است که در جاهای دیگر بنویسیم .% البته بجز کاراکتر ^ که اگر در اولین کاراکترِ بعد از کلوشه بیاید (یعنی داخل کلوشه حتما باید باشد برعکس magic کاراکترها) به معنای نقیض آن پترن یا کلاس کاراکتر هست
*** باز هم دقت کنیم که magic کاراکتر ها (مثلا علامت + و غیره که گفته شد) که یک کاراکترِ بعد از کلوشه بیایند ، همه ی پترن ها و کلاس کاراکترهای بکار رفته شده در کلوشه ی قبلی خود را بصورت مجموع بکار میبرند یعنی مثلا اگر در کلوشه ، عبارت


"([%a%d]+)"


بیاید و یک کاراکترِ بعد از کلوشه علامت + بیاید ، به این معناست که هم هر کجا کاراکترهای حروف و یا کاراکترهای عدد یافت شد ، صرف نظر از ترتیب آنها ، مجموع این کاراکترها در خروجی ذخیره میشود (علمت پرانتز هم که قبلا کار شد و برای ذخیره کردن آن پترن در متغییر تابع هست
*** تذکر : در داخل کپچرِ کلوشه ، اگر کلاس کاراکترِ نقطه (که در جاهای دیگر به معنای گرفتن و برگرداندن همه ی کاراکترها بود) بیاید ، فقط به معنای یافتن برای کاراکترِ نقطه هست یعنی دیگر همه ی کاراکترها را برنمیگرداند




17)



MainStr = "this year is 2015 + (christian / 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "([%d+%a-])",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


همانطور که گفته شد در پترن ، ابتدا و انتها پرانتز آمد یعنی کل جواب یافت شده ، در متغییر Content1 ذخیره شود
بعد از آن کلوشه آمد بدیت معناست که هر چه داخل این کلوشه هست ، ترتیب شان مهم نیست یعنی این طور نیست که چون ابتدا d% آمد ، اگر در رشته ی اصلی عدد ، بعد از یک رشته یا هر کاراکتر دیگری مثل space یا کاراکترهای سجاوندی و ... آمد ، نتیجه ای یافت نشود پس فقط کافی است پترنی را که در کلوشه آمد ، در هر کجای رشته ی اصلی پیدا کند تا آنرا برگرداند یعنی d% که همان عدد هست و یا a% که همان حروف ها هستند را در هر کجای رشته ی اصلی پیدا کند تا آنرا برگرداند
اما نکته ی بسیار حائز اهمیت اینجاست که در پترن ، بعد از d% علامت + آمد و قبلا آموخته بودیم که اگر یک کاراکترِ بعد از کلاس کاراکترها (همان جدولی که در ابتدای آموزش برسی شد که شامل a% و d% و ... بودند) ، magic کاراکتر ها (مثبت و منفی و غیره) بیایند (مثلا فرض کنیم علامت + بیاید) ، مجموع همان نوع کاراکتر (در اینجا باید مجموع کاراکتر های عددی) را برگرداند اما همانطور که گفته شد ، در کلوشه ها ، این مفهوم معنا ندارد و چنین نمیشود حتی مثل همین مثال که بصورت +d% آمد ، مجموع کاراکترهای عددی را برنمیگرداند و انگار مثل حالتی که علامت + را نگذاریم ، فقط هر بار تک تک کاراکترهای عددی را برمیگرداند چرا؟ چون داخل کلوشه ، باید magic کاراکترها بعد از کلوشه بیایند (نه قبل از کلوشه و نه داخل کلوشه) تا مفهومِ مجموع کاراکترهای هم نوع داشته باشند
اما پس در این مثال ، کاراکتر مثبت + که بعد از d% آمد ، اگر به معنای مجموع کاراکترهای عددی نیست ، به چه معناست؟
اگر magic کاراکترها هر جایی جز یک کاراکتر بعد از کلوشه بیایند ، مثلا اگر داخل کلوشه مثل همین مثال بیایند ، فقط به معنای یافتن همان کاراکتر است ( در این مثال علامت + بعد از d% یعنی اگر در رشته ی اصلی ، کاراکتر + وجود داشت ، آنرا پیدا کن) که قبلا با اضافه کردن % قبل از آن کاراکتر ، این کار را انجام میدادیم یعنی در اینجا +d% به این معناست که قبلا مینوشتیم +%d%
همینطور هر چند بعد از a% هم magic کاراکترِ منفی - آمد و معنای خاصی دارد ، اما چون جایی جز یک کاراکترِ بعد از کلوشه آمد (یعنی چون داخل کلوشه آمد) مانند کاراکتر + به معنای آن است که در رشته ی اصلی هر کجا کاراکتر - دیده شد ، برگردانده شود
پس در کل در این پترن ، صرف نظر از ترتیب پترن ها ، بصورت مجزا و تک تک ، به دنبال کاراکترهای مثبت + و منفی - و عدد و حروف میگردد در رشته ی اصلی و حتی اگر وجود هم نداشت ، اشکالی ندارد مثلا چون در رشته ی اصلی کاراکتر منفی - وجود ندارد ، علامت منفی پیدا نمیشود پس اولین تک کاراکتر در رشته ی اصلی که رشته ی t است بررسی میشود که جزء کاراکترهای + یا - یا d% یا a% هست یا نه و چون جزء a% یعنی حروف هست ،در متغییر Content1 برگردانده میشود (گفتیم ترتیب کاراکترها مهم نیست و نه اینکه a% چون در پترن در سومین کاراکتر بود ، پس مثل قبل وقتی سومین کاراکتر این نوع بود باید یافت شود چون داخل کلوشه هست و ترتیب مهم نیست) و همین طور اگر بررسی کنید همه ی کاراکترهای رشته ی اصلی بجز space ها و پرانتز و اسلش ، بقیه بصورت تک تک برگردانده میشود



18)



MainStr = "this year is 2015.11.29 (christian 1394+.....1396 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "([%d.+]+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


کل پترن داخل پرانتز هست پس کل نتیجه دی پیدا شده داخل متغییر Content1 ذخیره میشود. همانطور که گفته شد اگر جایی پترن +. میآمد یعنی همه ی کاراکترها برگردانده شود اما چون داخل کلوشه آمدند یعنی فقط کاراکترهای نقطه . و مثبت + جستجو شوند پترن d% هم که عددها را برمیگرداند پس در این مثال هر کجا کاراکترهای نقطه و مثبت و عدد پیدا شدند ، برگردانده میشوند. حالا پترن های موجود در کلوشه تمام میشوند و دقیق کاراکترِ بعد از کلوشه ، magic کاراکتر + هست که این علامت در اینجا به معنای این هست که هر پترن یا کاراکتر یا کلاس کاراکتر (کلا هر چیزی) داخل کلوشه وجود دارد ، بصورت مجموع همان کاراکترها برگردانده شود (دقیق همان علامت + ای که قبلا کار کردیم) اما تفاوت اش این است که شامل همه ی پترن های داخل کلوشه میشود یعنی داخل کلوشه که 3 پترن وجوددارد و d% هست یعنی کلیه ی کاراکترهای عددی که یافت شد را برگرداند و کاراکتر نقطه یعنی هر چند تا کاراکتر نقطه که یافت شد ، پشت سر هم برگرداند و کاراتر + هم همینطور یعنی هر چند تا کاراکتر + که دیده شد ، را با هم برگرداند
پس در این مثال در ابتدا عدد 2015 دیده شد و برگردانده میشود و در ادامه ی این بازگرداندن ، چون علامت نقطه وجود دارد هم برگردانده میشود (تا حالا شد .2015 برگردانده شد) و در ادامه ی آن نقطه ، عدد 11 هست که برگردانده میشود (تا حالا شد 2015.11 برگردانده شد) و در ادامه هم نقطه و در ادامه ی آن هم عدد 29 برگردانده میشود پس در اولین اجرای حلقه ، رشته ی "2015.11.29" برگردانده میشود
در اجرای دوم هم که مشخص هست عدد 1394 و در ادامه چون کاراکتر + در پترن وجود داشت ، برگردانده و در ادامه هم نقطه های پی در پی (چون علامت + را بعد از کلوشه گذاشتیم ، همانطور که گفته شد ، کاراکترهای مجموع نقطه ها را هم شامل میشد) و در ادامه هم عدد 1396 پس در اجرای بار دوم رشته ی "1396.....+1394" برگردانده میشود



19)



MainStr = "this year is 2015.11.29 (christian 1394+.....1396 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "([^%d.+]+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


در این پترن ، فقط ابتدای بعد از کاراکتر کلوشه ، کاراکتر ^ قرار داده شد . هر گاه این کاراکتر در اولین کاراکترِ بعد از باز کردن کلوشه بیاید (نه جای دیگر مثلا قبل ی بعد از آن) ، کل کلوشه را نقیض آنرا برمیگرداند یعنی در مثال شماره ی 18 که جواب در دفعه ی اول میشد "2015.11.29" و در دفعه ی دوم میشد "1396.....+1394" این بار نقیض آن یعنی همه ی کاراکترها جز این کاراکترها میشود یعنی جواب در 3 بار تکرار حلقه ، ابتدا رشته ی "this year is " یعنی تا فضای خالیِ قبل از عدد 2 و دفعه ی بعدی حلقه ، رشته ی



(christian


که از کاراکتر فضای خالیِ بعد از عدد 9 و تا کاراکتر فضای خالیِ قبل از عدد 1 هست و سومین بار هم رشته ی



solar)


یعنی از کاراکتر فضای خالیِ بعد از عدد 6 شروع تا آخر ، برگردانده میشود



20)



MainStr = "this year is ^2015.11.29+ (christian 1394+.....1396 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(MainStr, "(^[%d.+]++)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


همانطور که قبلا کاراکترهایی که مینوشتیم ، باید پی در پی و به ترتیب و از همان نوع میبودند ، هر کاراکتر یا کلاس کاراکتری که خارج از علامت کلوشه باشد ، باید ترتیب مثل قوانین قبل رعایت شود . در این مثال هم چون قبل از کلوشه کاراکتر ^ قرار دارد پس یعنی همین کاراکترِ ^ در رشته جستجو میشود ( شبیه همان ^% ) و حتما اگر در کاراکترِ بعد از آن مواردی که در کلوشه وجود دارد ، در رشته هم موجود بود (که در مثال های بالا بررسی کردیم) و بعد از کلوشه هم که چندین بار اشاره شد که فقط اولین magic کاراکترِ بعد از کلوشه (که در مثال بالا علامت + هست) به معنای مجموع همه ی کاراکترهای هم نوع داخل کلوشه هست (که بررسی کردیم در مثال های قبلی) ولی دومین علامت + بعد از بسته شدن کلوشه ، به معنای جستجو برای کاراکترِ + در رشته ی اصلی هست و چون این کاراکتر در خارج از کلوشه قرار دارد پس بسیار مهم است که اولین کاراکترِ بعد از حاصل داخل کلوشه (که حاصل و نتیجه و مقدار برگشتی داخل کلوشه همان مقدار رشته ی 2015.11.29 هست برای بار اول) ، کاراکتر مثبت در رشته ی اصلی باشد تا جواب حاصل شود پس جواب میشود رشته ی "+2015.11.29^"
چون ترتیب کاراکتر ^ قبل از مقدار کلوشه و کاراکترِ + بعد از مقدار کلوشه ، برای حاصل دومین تکرار برای خود کلوشه که رشته ی "1396.....+1394" هست ، وجود ندارد یعنی قبل از رشته ی "1396.....+1394" علامت ^ و بعد از آن علامت + وجود ندارد ، پس این مقدار دیگر برگردانده نمیشود



21)



MainStr = "this *year is 2015.11.29 (christian 1394+1396 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1, Content2 = string.find(MainStr, "([%d%p])([%a%s]+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


در این مثال دو تا پرانتز آمد پس محتوای هر پرانتز ، داخل هر متغییر جداگانه (دو متغییر) ذخیره میشود. دو کلوشه ی مجزا آمد یعنی حتما ابتدا باید کلوشه ی اول یافت شود و باز هم حتما دقیق کاراکترِ بعدی آن ، کلوشه ی دوم باید باشد تا نتیجه حاصل شود
چون در اولین کلوشه ، آخر آن (یعنی کاراکترِ بعد از بسته شدن اولین کلوشه که سمت چپ هست) هیچ علامتی (magic کاراکتر مثل مثبت و غیره) نیامد ، پس محتوای این کلوشه هر چه که یافت شود ، تک کاراکتری هست (نه مجموعی از آن کاراکتر) ولی چون دو دومین کلوشه ، آخر آن (یعنی کاراکترِ بعد از بسته شدن دومین کلوشه که سمت راست هست) magic کاراکتر مثبت آمد ، پس همه ی پترن ها و محتوای داخل دومین کلوشه ، بصورت مجموع همان نوع کاراکتر یافت میشوند
محتوای کلوشه ی اول d%p% هست یعنی هر کجا که کاراکترهایی از نوع عدد و یا از نوع کاراکترهای سجاوندی (مانند کاراکترهای .^&()[]×~+*/@#$% و از این دست کاراکترها) یا از هر دو نوع ، آن هم بصورت تک کاراکتری دیده شد که دقیق ، کاراکترِ بعد از آن ، محتوای دومین کلوشه که a%s% هست یعنی کاراکترهایی از نوع حروف یا از نوع فضای خالی یا هر دو باشد (بصورت مجموع یا تکی) باشد ، برگردانده شود
اولین جایی که کاراکتر عددی یا سجاوندی دیده میشود ، کاراکتر ستاره در ششمین کاراکترِ رشته ی اصلی هست و دقیق کاراکترِ بعدی آن هم که یا میبایست حروف یا فضای خالی یبود ، کاراکترِ حروفی هست بنابراین تا جایی که کاراکترهای حروف و هم کاراکتر فضای خالی (ترتیب شان فرقی ندارد ولی فقط باید فقط همین دو نوع کاراکتر که داخل کلوشه هست ، باشند) ادامه یابد ، همان رشته در متغییر ذخیره میشوند که در اولین اجرا از کاراکتر y شروع و تا کاراکترِ فضای خالی که قبل از عدد 2 هست ادامه میابند و چون به عدد 2 که رسید ، هیچ کدام از نوع عدد یا space نیست ، پس جواب در اولین اجرای حلقه خاتمه میابد
در اجرای بعدی ، جستجو دوباره از عدد 2 شروع میشود . هر چند عدد 2 از نوع عددی هست (یکی از انواع عددی یا سجاوندی که باید میبود) ولی کاراکترِ بعد از آن که صفر هست ، از نوع حروف یا space نیست و یا هر چند نوزدهمین کاراکتر که نقطه هست ، از نوع سجاوندی هست ولی کاراکتر بعد از آن هم از نوع حروف یا space نیست پس جلوتر میرویم به همین گونه و به عدد 9 که رسیدیم ، کاراکترِ بعد از آن از نوع space هست پس این دو مقدار هم برگردانده میشوند
در سومین اجرای حلقه ، به کاراکتر سجاوندی پرانتز باز برمیخوریم که بعد از آن حروف آمد تا قبل از عدد 1
در چهارمین اجرای حلقه ، بعد از عدد 6 ، کاراکترهای space و حروف آمد تا قبل از کاراکتر پرانتز بسته که حساب میشود



22)



date = "17.492.03 Array.Madule(arr1)^245_54/x-1+Ywq*365Int +space\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1, CONT = string.find(date, "([%d%.(%s)_])",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..CONT, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end



این مثال ارور میدهد چون نمیشود از چندین کپچرِ پرانتزِ داخل هم استفاده کرد چون s% هم باز جداگانه داخل یک کپچرِ پرانتزِ دیگر قرار گرفت و کپچرهای تو در تو جواب نمیدهد اما میشود که از متغییر CONT استفاده نکرد یعنی این طور نشود که s% را به عنوان محتوای دیگر حساب کند و داخل متغییر جداگانه ای بریزد
پس در این مثال (در صورت پاک کردن متغییر CONT) ، پرانتزهای کنار کلاس کاراکتر s% به معنای یافتن کاراکتر پرانتز داخل رشته ی اصلی هست



بگذارید برای تفهیم بهتر کلاس کاراکترها که باید بیشتر در همان ایتدا میپرداختیم و زیاد نپرداختیم ، بپردازیم :

23)



date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(%p+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


برای درک بهتر ، مجبوریم رشته ای با تقریبا تمام کاراکتر بنویسیم
هر چند در پترن ، فقط +p% را نوشتیم اما چون نمیشود هر بار بدنه ی مثالی به این بزرگی را با هر کلاس کاراکتر تکرار کرد لذا فقط در توضیحات این مثال ، کلاس کاراکترها را تغییر میدهیم و شما هم طبق توضیحات ، هر بار پترن مورد نظر را تغییر دهید تا نتیجه ی آنرا مشاهده و تجربه کنید
الان که +p% نوشته شد ، یعنی همه ی کاراکترهای سجاوندی که همه ی کاراکترهای علائم دار هستند را برمیگرداند مثل کاراکترهای نقطه و مثبت و غیره .+-*/÷×!@#$%^&*)(_+=?
همانطور که در ابتدای آموزش اشاره شد ، هر کدام از کلاس کاراکترها را اگر با حرف بزرگ آن بنویسیم ، نقیض آن چیزی را که باید پیدا کند ، پیدا میکند یعنی همه ی کاراکترها ، جز آنهایی که بصورت عادی هستند مثلا اگر در مثال بالا ، بنویسیم +P% همه ی کاراکترها بجز کاراکترهای سجاوندی یعنی بجز کاراکترهای .+-*/÷×!@#$%^&*)(_+=? و امثال این کاراکترها ، برگردانده میشود
کلاس کاراکتر نقطه که همه ی کاراکترها را برمیگرداند (این کلاس کاراکتر دیگر نقیض ندارد بلکه فقط میشود محدوده ای برای این کاراکتر مشخص کرد که از کدام کاراکتر تا کدام کاراکتر ، همه ی کاراکتر بین این دو کاراکترِ شروع و پایان را برگرداند)
کلاس کاراکتر a% هم که تمام کاراکترهای حروفی را فقط برمیگرداند (حتما در پترن بالا ، همه ی این کلاس کاراکترها را که توضیح میدهیم ، تست کنید) یعنی بقیه ی کاراکترهای دیگر اعم از عدد و space و ... را شامل نمیشود
کلاس کاراکتر d% همه ی کاراکتر عددی را برمیگرداند (پر واضح هست که D% همه ی کاراکترها بجز عدد را برمیگرداند)
کلاس کاراکتر l% (حرف اِل هست نه حرف یک) همه ی کاراکترِ حروفی (نه بقیه ی انواع کاراکترها) که آنهم فقط با حروف کوچک نوشته شدند را برمیگرداند (پر واضح هست که L% همه ی کاراکترهای حرفی که کوچک نیستند را برمیگرداند)
کلاس کاراکتر u% همه ی کاراکترهای حروفی که با حروف بزرگ نوشته شدند را برمیگرداند (پر واضح هست که L% همه ی کاراکترهای حرفی که بزرگ نیستند را برمیگرداند)
کلاس کاراکتر s% همه ی کاراکترهای فضای خالی یا همان space را برمیگرداند (پر واضح هست که S% همه ی کاراکترهای غیر از space را برمیگرداند)
کلاس کاراکتر مهم w% همه ی کاراکترهای هم حروف و هم عددی را برمیگرداند(حتما این را تست کنید) یعنی کار کلاس کاراکترهای a% و d% را با هم یکجا انجام میدهد (پر واضح هست که W% همه ی کاراکترهای غیر ازحروف و عدد را برمیگرداند)
کلاس کاراکتر x% هم عددهای شانزده دهی (که شامل 0 تا 9 و ABCDEF هستند) را برمیگرداند (X% هم که مشخص هست)









Magic کاراکترهای منفی و ضرب - و * :
مهم ترین و شاید هم اندکی سخت تر و البته بخش جذاب پترن ها ، همین Magic کاراکتر های - و * هست. Magic کاراکتر مثبت که قبلا توضیح داده شد
این Magic کاراکترها (منفی و ضرب) هر چند گاها عملکردی شبیه Magic کاراکتر مثبت دارند اما ماهیت کاملا متفاوتی دارند. Magic کاراکترهای ضرب و منفی ، دو وظیفه را انجام میدهند
Magic کاراکترهای منفی و ضرب ، وظیفه ی الویت بندی پترن کاراکتر یا کلاس کاراکتر (کلا آن قسمت خاص پترن) مربوط به خود را در وهله ی اول دارند بدین معنا که اگر Magic کاراکتر منفی مثلا اگر برای کلاس کاراکتری بیاید ، ابتدا آن کلاس کاراکتر مورد بررسی و جستجو قرار نمیگیرد یعنی ابتدا کلاس کاراکتر یا کاراکتری که الویت برتری دارد (مثلا کاراکتر یا کلاس کاراکتری که Magic کاراکترِ مثبت دارد که الویت برتری دارد ، ابتدا مورد جستجو قرار میگیرد) و حتی اگر آن کلاس کاراکتر مربوط به Magic کاراکتر منفی هم موجود نبود ، مشکلی وجود نخواهد داشت
تذکر مهم : الویت Magic کاراکترها این طور هست که کلاس کاراکتر (منظور a% و ...) یا کاراکتری که اصلا Magic کاراکتر (منظور + و - و *) ندارد باکلاس کاراکتر یا کاراکتری که Magic کاراکترِ مثبت دارد ، هم الویت هستند و بعد از آن ، به ترتیب الویت با Magic کاراکترِ ضرب و بعد از آن هم الویت با Magic کاراکترِ منفی هست یعنی ترتیب الویت ها بصورت + و * و - هست
وظیفه ی دوم این Magic کاراکترهای منفی و ضرب ، شبیه به مثبت هست یعنی مجموع هم نوع آن کلاس کاراکتر یا کاراکتر را برمیگردانند اما تفاوت هایی وجود دارد . Magic کاراکترهای مثبت و ضرب ، بزرگترین جمله (یا نمونه) را برمیگردانند ولی Magic کاراکتر منفی ، کوچک ترین جمله (نمونه) را
مثلا اگر پترنی بنویسیم که در رشته ای ، هر کاراکتری که بین کاراکترهای گیومه " هست را برگرداند ، ولی در رشته ی مورد نظر ، 3 تا از این کاراکترها داشته باشیم ، اگر از Magic کاراکتر مثبت یا ضرب استفاده کنیم ، بین اولین تا آخرین (سومین) کاراکترهای گیومه " هر کاراکتری هست را برمیگرداند اما وقتی از Magic کاراکتر منفی استفاده میکنیم ، بین کوچک ترین جمله (نمونه) که جملات بین دو گیومه ی اول هست (بین گیومه ی اول و دوم) را برمیگرداند و در اجرای بار بعدی حلقه ، جملات و کاراکترهای بین گیومه های بعدی (که گیومه های دوم و سوم) هست را برمیگرداند
برای Magic کاراکترهای هم نوع (که بدیهی هست الویت یکسانی دارند) ، پس از چک کردن بصورت ترتیبی ، اگر هر کدام از آنها موجود نبودند ، اشکالی ندارد
** تذکر بسیار مهم : چون اولین وظیفه ی Magic کاراکترهای منفی و ضرب ، الویت بندی هست (Magic کاراکتر مثبت که همیشه اولین الویت را دارد ، منظورمان نیست) پس فقط یک کاراکتر یا کلاس کاراکتر در پترن ی این Magic کاراکترها را داشته باشند ، مفهوم ندارد و اغلب باعث پیدا نشدن و ارور میشود یعنی مثلا اگر در کل پترنی ، فقط بنویسیم "-d%" چون فقط یک کلاس کاراکتر با Magic کاراکتر منفی آمد ، معنا ندارد . یعنی مثلا باید با یک مجیک کاراکتر دیگر یا بدون کاراکتر بیاید که الویت ها مشخص شود مثلا "-d%p%" بیاید

پس تعریف کامل Magic کاراکترهای مثبت و منفی و ضرب :
مثبت + : اولین و بالاترین الویت را در جستجو در رشته ی اصلی دارد و بزرگترین جمله (نمونه) را برمیگرداند
ضرب * : دومین الویت را در جستجو در رشته ی اصلی دارد و بزرگترین جمله (نمونه) را برمیگرداند
منفی - : سومین الویت را در جستجو در رشته ی اصلی دارد و کوچک ترین جمله (نمونه) را برمیگرداند

تذکرات :
اندکی قلق در Magic کاراکترها هست که بنده به تمام آن پی نبردم ولی تذکراتی که طی تجربه (علمی نیست) میتوانم بدهم این است که :
1) تا میتوانید سعی کنید از قرار دادن Magic کاراکترهای متفاوت در یک پترن ، خودداری کنید (یعنی سعی کنید بیشتر از دو نوع Magic کاراکتر را در یک پترن نیاورید)
2) وقتی از کلاس کاراکترِ نقطه برای یافتن بین دو کاراکترِ خاص استفاده میکنید ، سعی کنید برای این کلاس کاراکترِ نقطه ، Magic کاراکتری که الویت بالاتری نسبت به کاراکتر یا کلاس کاراکترِ اطراف این کلاس کاراکترِ نقطه دارند ، استفاده کنید یعنی برای کلاس کاراکترِ نقطه ، از بالاترین الویت از Magic کاراکترها استفاده کنید
3) سعی کنید چندان از Magic کاراکتر هم نوع ، برای کاراکتر یا کلاس کاراکترهای مختلف استفاده نکنید



24)



s = [[then he said: "it's all right" so he got " to the her home" and do see tv!]]
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(s, "\"(.+)\"", LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


خوب در مثال ها دیگر لازم نیست به الویتِ کاراکترها بپردازیم . الان در این مثال در پترن \" که اول و آخرِ پترن آمد ، چون Magic کاراکتری ندارد با قسمت وسط که کلاس کاراکترِ نقطه ، Magic کاراکترِ مثبت دارد (همانطور که گفته شد) الویتی دقیق برابر هم دارند و هر گاه الویت ها برابر بودند ، دقیق مثل حالت قبل ، باید ترتیب کاراکترها و کلاس کاراکترها و کپچرها ، حفظ شوند) پس مثل روال قبل ، باید ترتیب پترن ها رعایت شود یعنی ابتدا کاراکتر گیومه " جتسجو میشود و تا گیومه ی آینده ، هر کاراکتری بین شان برگردانده میشود
اما در کل 3 تا گیومه داخل رشته ی اصلی قرار دارد . یعنی همان نمونه ، بصورت متوالی تکرار شده پس باید از کدام گیومه تا کدام گیومه ، کاراکترها برگردانده شوند؟
اگر Magic کاراکترِ مثبت باشد ، یعنی بزرگترین جمله یعنی از اولین تا آخرین گیومه (نمونه) برگردانده شود (فرضا اگر 1000 تا گیومه وجود داشت ، باز هم همه ی کاراکترهای بین اولین تا آخرین گیومه برگردانده میشدند) که در این مثال ، رشته ی it's all right" so he got " to the her home برگردانده میشود
حالا فرض کنید در پترن ، برای کلاس کاراکترِ نقطه ، بجای Magic کاراکترِ مثبت ، Magic کاراکترِ منفی بود ، چه میشد؟ یعنی اگر پترن این مثال بصورت :



"\"(.-)\""


خوب خیلی های شما شاید درست حدس زده باشید اما همانطور که گفته شد ، Magic کاراکترها 2 تا وظیفه دارند که باید هر دوی آنها را حتما چک کنیم)
اولین وظیفه ی Magic کاراکترها ، تعیین الویت برای جستجو بود . برای پترن هایی که Magic کاراکتر ندارند و یا Magic کاراکترِ مثبت دارند (مثل همین چند خط بالا) چک نمیکنیم چون حتما باید ترتیب شان رعایت شود چون بالاترین الویت را دارند ولی برای بقیه ی Magic کاراکترها دانه دانه باید چک شود
چون در اینجا -. از الویت کمتری نسبت به بقیه ی کاراکترهای اطراف خود (منظور پترن های \" در اطراف خود یا هر پترن دیگری) برخوردارند ، پس در ذهن مان آنرا بصورت موقت حذف میکنیم پس چیزی که باقی میماند ، میشود :



"\"\""


حالا این را معنی میکنیم یعنی چه؟ یعنی هر جا گیومه " دیده شد (کاراکتر شماره 15) تا هر جایی که باز هم همین کاراکتر دیده شد (چون از ذهن مان حذف کردیم ، به این معنا ترجمه نکنیم که هر جایی که دو گیومه ی پی در پی دیده شد یعنی اینکه حتما دو تا گیومه باید پی در پی باشند تا حاصل پیدا شود چون -. بین شان وجود داشت) که کاراکترهای شماره ی 27 و 44 هستند. برای اینکه حالا مشخص کنیم کدام گیومه ، دوباره همان کلاس کاراکتر -. را که از ذهن مان حذف کرده بودیم ، برمیگردانیم و مهم ترین نکته اینجاست که اگر این کلاس کاراکتری که در این مثال magic کاراکترِ منفی گرفت ، بین این دو گیومه (یعنی سرجایی که قرار بوده باشد) ، وجود داشت که برگردانده میشود کاراکتراهیی که وجود داشتند ولی اگر وجود هم نداشت ، مشکلی نیست و بجایش یک رشته ی خالی یعنی "" برگردانده میشود چون الویت کمتری دارند (چه منفی یا ضرب که الویت کمتری دارند همینطورند) که منفی به معنای کوچکترین جمله و نمونه بود پس منظورش گیومه ی بعد از گیومه ای که پیدا شد (گیومه ی شماره 2) هست و در دومین تکرار حلقه ، ادامه ی گیومه های بعدی برگردانده میشود (در اینجا گیومه ی 2 تا 3) پس در حلقه ی اول ، رشته ی it's all right" so he got و در حلقه ی تکرار دوم ، رشته ی to the her home برگردانده میشود
درست است که این روش ، تفاوتی در نتیجه ی یافتن جواب در این مثال نداشت (یعنی از همان ابتدا مشخص بود که کوچک ترین جمله کدام است و نیاز نبود در ذهن مان چیزی را حذف کنیم) اما در مثال های دیگر اگر به این شیوه بررسی نشود ، درست نیست و به جواب نمیرسیم



25)



date = "17.492.03 Array.Madule(arr1)^245_54/x-1+Ywq*365Int +space\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(%d-%a-%s)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end



در این مثال ، همانطور که گفته شد ، کاراکترهایی که Magic کاراکترِ منفی یا ضرب دارند (در این مثال d% و a%) را در ذهن مان حذف میکنیم پس فقط s% میماند . پس به دنبال کاراکترهای فضای خالی در رشته ی اصلی میگردیم آن هم هر بار فقط یک کاراکترِ فضای خالی (چون s% هیچ Magic کاراکتری ندارد)
اولین کاراکترِ space در کاراکتر شماره ی 10 هست پس این کاراکتر فضای خالی برگردانده میشود (حالا در ذهن مان که حذف کرده بودیم d% و a% را برمیگردانیم) . حالا همانطور که گفته شد ، چون هر دو منفی هستند (هم d% و هم a%) پس هم الویت هستند پس اگر هر دو وجود داشتند یا نداشتند یا یکی از اینها وجود داشتند یا نداشتند ، مهم نیست (هر چند در نکات بیان شد که یک Magic کاراکتر در چند پترن یا کلاس کاراکتر تکرار نشود بهتر هست ولی از این قضیه هم در این مثال استفاده کردیم) و باز هم چون قبل از s% کلاس کاراکتر a% آمد (و هم الویت با d% هست) پس ابتدا این مورد چک میشود و در صورت وجود یا عدم وجود a% ، کلاس کاراکتر d% چک میشود
پس میگردیم ، ببینیم که یک کاراکترِ قبل از این فضای خالی (که در کاراکتر شماره ی 10 بود) ، قبل از آن کوچک ترین نمونه مجموع حروف (مجموع هست چون همه ی Magic کاراکترها این خاصیت را دارند و کوچک ترین نمونه هست چون Magic کاراکترِ منفی هست) آمد یا نه . قبل از این فضای خالی ، عدد 3 آمد که از نوع حروف یا a% نیست پس اشکالی ندارد . پس حالا میچرخیم برای کلاس کاراکتر قبل از آن که d% بود یعنی برای عدد . یعنی میچرخیم ببینیم که کاراکترِ قبل از این فضای خالی (که کاراکترِ شماره ی 10 بود) ، مجموع عددی (کوچک ترین نمونه) آمد یا نه که میبینیم این طور هست و مجموع آن ، همان رشته ی 03 ی قبل از آن است پس در اولین اجرای حلقه ، 03 و بعد از آن ، فقط یک کاراکترِ فضای خالی برگردانده میشود (دقت کنید چون در خروجی ، کارکتر فضای خالی ، در سمت راست هست پس برای ما انگار نامرئی هست که میتوانیم مثلا در حلقه ی دیگری ، تعداد کاراکترهایی که هر بار که این حلقه برمیگرداند را نمایش دهیم یا هر کاراکتر را جداگانه نمایش دهیم تا بهتر درک کنیم) پس در اولین اجرا ، رشته ی :



"03 "


برگردانده میشود .
حالا حلقه به اجرای دوم خود میرود و باز همین همین قضیه تکرار میشود یعنی ابتدا در ذهن مان d% و a% را حذف میکنیم پس کاراکتر فضای خالی که در کاراکترِ شماره ی 51 هست ، برگردانده میشود . حالا این a% را که از ذهن مان حذف کرده بودیم را برمیگردانیم ابتدا
پس میبینیم که یک کاراکتر قبل از این کاراکترِ فضای خالی ، آیا مجموع کاراکترهای حروفی وجود دارد؟ بله که میشود رشته ی "Int" و حالا d% را که از ذهن مان حذف کرده بودیم را قبل از این رشته ای که پیدا شد (یعنی قبل از کاراکتر رشته ی "I" که همان کاراکتر شماره ی 48 هست) برای وجودش چک میکنیم یعنی میبینیم که قبل از این کاراکتر شماره ی 48 ، مجموع کاراکترهای عددی وجود دارد؟ که جواب مثبت هست و رشته ی "365" هست . پس در اجرای بار دوم حلقه ، رشته های "365" و "Int" و یک کاراکترِ فضای خالی بعد از آن برگردانده میشود (حواسمان به کاراکتر فضای خالی باشد) یعنی میشود :



"365Int "


در اجرای بار سوم حلقه ، مشخص هست که باز هم برای فضای خالی (کاراکترِ بعد از کاراکتر شماره ی 51) جستجو میشود . باز هم به همین منوال . خود کاراکتر شماره ی 52 ، فضای خالی هست که برگردانده میشود . در قدم بعدی ، برای مجموع حروف a% قبل از این کاراکتر (یعنی قبل از کاراکترِ شماره ی 52 که میشود 51) جستجو میشود که کاراکتر شماره 51 چون فضای خالی هست و حروف نیست پس a% برای آن بی نتیجه هست . بنابراین برای مجموع عدد a% در قبل از کاراکتر space (یعنی قبل از کاراکترِ شماره ی 52 که میشود 51) جستجو میشود که همانطور که میدانید ، کاراکتر شماره 51 چون فضای خالی هست و عدد نیست پس d% برای آن بی نتیجه هست پس هم d% و هم a% برای آن یافت نمیشود که اشکالی ندارد چون Magic کاراکتر با الویت پایین داشتند . پس در اجرای بار سوم ، فقط همان تک کاراکترِ فضای خالی که ابتدا برگردانده شده بود ، برگردانده میشود
در اجرای حلقه در دفعات بعدی (تا اجرای بار هفتم) هم همینطور فقط هر بار تک کاراکترهای فضای خالی (که بین کاراکترهای شماره ی 51 تا 58 هستند) برگردانده میشود
در اجرای بار هشتم حلقه ، عرض شود که در این مثال ، رشته ی "n\" در رشته ی اصلی را به عنوان فضای خالی شناسایی میکند (چرا را دقیقا نمیدانم) پس با همان روال ، رشته ی :



"space "


برگردانده میشود



26)



date = "17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(.*)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


خوب مشخص هست که این پترن ، تمام رشته ها را انتخاب میکند یعنی در اجرای بار اول ، رشته ی :



"17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^
*"


برگردانده میشود (بجای n\ به خط بعدی میرود و بجای a\ چیزی شبیه ستاره چاپ میشود که بدیهی است)
اما طبق نکاتی که برای Magic کاراکترها گفته شد ، در اجرای دفعه ی بعدی با آنکه تمام مقادیر برگردانده شدند و کاراکتر دیگری در رشته ی اصلی وجود ندارد ، چون یک Magic کاراکتر با الویت کمتر (ضرب) را فقط برای یک کلاس کاراکتر آوردیم و کلاس کاراکتر دیگری با الویت متفاوت نیاوردیم ، در دفعات بعدی هم اجرا میشود (حلقه ی بی نهایت) که این بار برخلاف معمول ، متغییر LastChar یک واحد از متغییر FirstChar کوچکتر میشود (در دفعات حلقه ی بعد برای همیشه ) و متغیر Content1 هم تا آخر رشته ی خالی را برمیگرداند که همانطور که گفته شد یا باید از Magic کاراکترهای متفاوت در پترن استفاده کنیم یا شرط حلقه (if داخلی) را به صورت زیر تغییر دهیم :



date = "17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(.*)",LastChar+1)
if Content1==nil or (LastChar<FirstChar and Content1=="") then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end




27)



date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]*)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


این یکی از مثال های مهم هست (ولی نه اینکه سخت باشد)
میدانیم که Magic کاراکترها (مثبت و ضرب و منفی) برای کپچرهای کلوشه ، یک کاراکتر بعد از بسته شدن کلوشه میآیند واگر کاراکترهای + و * و - داخل یا قبل یا هر کجای دیگر جز جایی که گفته شد بیایند ، به عنوان کاراکتری برای جستجو در رشته ی اصلی در نظر گرفته میشوند
پس در پترن این مثال ، هر دو کلوشه ، Magic کاراکتر ضرب را دارند و چون Magic کاراکتر یکسانی دارند ، همانگونه که گفته شد ، به ترتیب چک میشوند یعنی ایتدا داخل کپچر کلوشه اول چک میشد و اگر وجود نداشت ، داخل عبارت بعدی آن یعنی داخل کلوشه ی بعدی چک میشود و ...
از قبل میدانیم ، داخل یک کلوشه ، پترنی نوشته شود ، ترتیب آن مهم نیست
پس ایتدا ، داخل کلوشه ی اول که s%p% هست ، در کاراکترِ اول در رشته ی اصلی جستجو میشود بدین معنا که کاراکتر اولِ رشته ی اصلی که 1 هست ، آیا از نوع فضای خالی هست؟ نه . از نوع کاراکترهای سجاوندی چطور؟ نه
پس برای کلوشه ی اول ، جوابی یافت نشد ولی چون الویت های دو کلوشه در یک سطح هست ، پس جواب ندادن یکی ، باعث میشود کلوشه یا همان عبارت بعدی جستجو شود (اما همانطور که گفته شد ، اگر در کل پترنی ، فقط عبارت هایی که با Magic کاراکتر های یکسان بکار برده شد ، باید حداقل برای یکی از آنها ، مقداری یافت شود یعنی نمیشود هیچ مقداری برای هیچ کدام یافت نشود) لذا در کارکتر اول رشته ی اصلی ، این بار (نه اینکه دو اجرای بعدی حلقه باشیم) کلوشه ی دوم که w% هست چک میشود. آیا کاراکتر اول که عدد 1 هست ، از نوع w% که همان حرف یا عدد هست ، هست؟ بله
پس چون Magic کاراکتر ضرب برای این کلوشه وجود دارد ، مجموع عبارت و کاراکترهای عدد و حروف را که در اجرای بار اول همان رشته ی "17" میشود ، برگردانده میشود
برای دفعات بعدی تکرار حلقه هم دقیقا به همین روش. بار دوم کاراکتر سوم در رشته ی اصلی که نقطه هست که از نوع یافت شده در کلوشه ی اول که همان p% هست پیدا شد . حالا این بار چون پیدا شد ، برای کلوشه ی دوم ، بک کاراکتر بعد از این کاراکتر (یک کاراکتر بعد از کاراکتر نقطه که همان کاراکتر شماره ی 4 که عدد 4 است) جستجو میشود یعنی جستجو میشود که عدد 4 ، جزء w% هست؟ بله پس کل نوع عدد و حروف (w%) انتخاب میشود که رشته ی "492" میشود . پس برای بار دوم برای کلوشه دوم که رشته ی نقطه بود و در ادامه ی آن رشته ی 492 برگردانده میشود که کلا میشود رشته ی "492."
بار سوم هم به همین ترتیب رشته "03."
بار چهارم باز هم کلوشه ی اول برای کاراکتر شماره 10 که فضای خالی هست جستجو میشود . چون جزء s% هست پس انتخاب میشود و کاراکترهای بعدی آن هم چون جزء کاراکترهای سجاوندی (در کلوشه ی اول) هستند ، انتخاب میشوند و کاراکتر بعد از آن هم که رشته ی "A" هست ، کلوشه ی دوم برای آن جستجو میشود. پس تا رشته ی "A38rray25" انتخاب میشود پس برای بار چهارم رشته ی :



"^_A38rray25"


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



".MaduleVersion5"


بار ششم :



"*(arr1"


هفتم :



") ^245"


هشتم :



"_54"


نهم :



"/x"


دهم :



"-1"


یازدهم :



"+Ywq"


دوازدهم :



"*+365Int"


سیزدهم :



" +space"


چهاردهم :



"^_"



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



date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]*)",LastChar+1)
if Content1==nil or (LastChar<FirstChar and Content1=="") then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end




28)



date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]-)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


این مثال مثل مثال قبل هست فقط در پترن ، برای کلوشه ی دوم ، بجای Magic کاراکترِ ضرب ، Magic کاراکترِ منفی قرار دادیم
خوب در این مثال چون Magic کاراکترِ ضرب بر Magic کاراکترِ منفی الویت دارد پس کلوشه ی دوم را از ذهن مان حذف میکنیم و کلوشه ی اول ، باید محتوایی برایش یافت شود اما اگر محتوایی برای اولین کلوشه یافت نشود ، چون کل پترن ، فقط از Magic کاراکترهای با الویت پایین (منفی و ضرب) استفاده شد ، در حلقه ی بی نهایت گیر میکند لذا کلوشه ی اول که s%p% هست ، در اولین کاراکتر رشته ی اصلی که 1 هست ، جستجو میشود و چون 1 کاراکتری از جنس s% و p% نیست پس حلقه در بی نهایت گیر میکند که برای رفع این مشکل ، یا باید شرط if داخل حلقه را مثل قبل درست کنیم یعنی بجایش بنویسیم :



if Content1==nil or (LastChar<FirstChar and Content1=="") then


یا اینکه در پترن ، از هیچ Magic کاراکتری استفاده نکیم که در این صورت ، کاراکتر مورد نظر ، الویتی برابر با Magic کاراکتر مثبت دارد یا اینکه از Magic کاراکتر مثبت استفاده کنیم مثلا پترن را این جوری بنویسیم که w% آخری هیچ Magic کاراکتی ندارد :



"([%p%s]*[%w]-%d)"


یا Magic کاراکترِ مثبت دارد :



"([%p%s]*[%w]-%d+)"


که در هر صورت معنا و هدف پترن عوض میشود پس این مهم است که از ابتدا ، روی الگوریتم درست در پترن ها کار کنیم
در همین مثال ، شماره ی 28 ، اگر در رشته ی اصلی همان ابتدا و به عنوان اولین کاراکتر ، یکی از کاراکتر از نوع هایی که در کلوشه ی اول بود را اضافه میکردیم (مثلا مینوشتیم "17_" یعنی یک آندرلاین همان ابتدا اضافه میکردیم) مشخص است که چون یکی از نوع های داخل کلوشه (از نوع p%) بود ، پس در اولین بار اجرای حلقه ، رشته ی "17_" برگردانده میشد ولی در اجرای دوم به بعد حلقه ، مثل حالت بالا که توضیح داده شده ، در حلقه ی بی نهایت گیر میکرد
*** پس این قضیه بسیار مهم است که همان ابتدا چک کنید ببینید در پترن تان آیا همه ی کاراکترهای تان ، Magic کاراکترهایی با الویت پایین دارند یا خیر یعنی اینکه چک کنید و ببینید همه ی کاراکترهایتان (اعم از کلاس کاراکتر و کپچرها و کاراکترهای معمولی و غیره) ، آیا Magic کاراکترِ منفی یا ضرب را فقط دارند یا نه؟ اگر جواب مثبت باشد پس با دستورات شرط عادی ، حلقه در بی نهایت گیر میکند



29)



date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end



این مثال مثل مثال قبل هست فقط در پترن ، برای کلوشه ی دوم ، بجای Magic کاراکترِ منفی، Magic کاراکترِ مثبت قرار دادیم
کلوشه ی اول ضرب و کلوشه ی دوم مثبت است . همانطور که میدانیم ، Magic کاراکترِ مثبت ، الویت بیشتری دارد پس در ذهن مان کلوشه ی اول را حذف میکنیم. پس نوع کلوشه ی دوم که w% یعنی حروف و عدد هست را میبینیم در اولین کاراکترِ رشته ی اصلی آمد یا نه؟
بله آمد پس چون Magic کاراکتر مثبت دارد ، همه و مجموع آن عدد را که میشود "17" انتخاب میشود. حالا کلوشه ی اول را که قبل از کلوشه ی دوم بود را دوباره در ذهن مان میاوریم . چون کلوشه ی اول ، قبل از کلوشه ی دوم هست ، پس یک کاراکتر قبل از اولین کاراکترِ محتوای کلوشه ی دوم (که 1 هست) را برای s%p% جستجو میکنیم و هیچ نتیجه ای یافت نمیشود (چون قبل از اولین کاراکتر ، کاراکترِ دیگری وجود ندارد) و چون ضرب ، الویت کمتری دارد نسبت به مثبت ، پس پیدا نشدن نتیجه ، هیچ اشکالی ندارد پس در اولین اجرای حلقه ، رشته ی "17" برگردانده میشود
در دومین اجرای حلقه هم همینطور. از سومین کاراکتر و از آن به بعد در رشته ی اصلی، دنبال حروف یا عدد که همان w% یا کلوشه ی دوم هست ، جستجو میشود که نتیجه کاراکتر چهارم تا تا ششم که "492" هست میشود . حالا قبل از اولین کاراکترش یعنی قبل از عدد 4 ، دنبال محتوای اولین کلوشه که s%p% بود جستجو میشود که قبل از آن ، نقطه که جزء کاراکترهای سجاوندی هست ، یافت میشود پس نتیجه رشته ی "492." میشود
در سومین اجرا هم به همین گونه رشته ی "03." برگردانده میشود
در چهارمین اجرا ، باز هم ابتدا دنبال کلوشه ی دوم که حروف یا عدد هست پس رشته ی "A38rray25" پیدا میشود و قبل از "A" در این رشته هم دنبال محتوای کلوشه ی اول گشته میشود و اگر وجود داشت ، لحاظ میشود پس یک space و کاراکترهای ^ و _ قبل از آن برگردانده میشود که کلا میشود رشته ی :



^_A38rray25


پنجمین اجرا :



".MaduleVersion5"


که اغلب شبیه مثال شماره ی 27 هست
نکته ی این مثال اینجاست که چون در این مثال از Magic کاراکتر با بزرگترین الویت یعنی Magic کاراکتر مثبت استفاده شد ، پس حلقه در بی نهایت گیر نمیکند (حتی اگر برای آن مقداری یافت نشود که در آن صورت جوابی پیدا نمیشود نه اینکه مثل قبلی ، در حلقه ی بی نهایت گیر کند)



30)



MainStr = "this year is 2015.11.29 (christian 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1, Content2, Content3 = string.find(MainStr, "([^%d.+]+)(.-)(%d*)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2 .."\n"..Content3, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


یکی از مهم ترین مثال هاست
پرانتز اول که همان کلوشه هم در آن هست ، magic کاراکترِ مثبت آمده که مشخص هست نسبت به پرانتز دوم که magic کاراکتر منفی و سومی که magic کاراکتر ضرب آمد ، الویت دارد پس در ذهن مان پرانتزهای دوم و سوم را حذف میکنیم
همانطور که در چندین مثال قبل بررسی شد ، جواب پترن :



([%d.+]+)


در 2 بار اجرا ، به ترتیب رشته های "2015.11.29" و "1394" میشد
اگر دقت کنید ، اول کلوشه علامت ^ آمد که گفتیم اگر در اینجا بیاید ، به معنای معکوس آن عبارت هست که در اینجا یعنی همه ی کاراکترها بجز رشته های "2015.11.29" و "1394" پس میشود 3 بار یعنی 3 رشته ی :



"this year is "


و



" (christian "


و



" solar)"


با احتساب فضای خالی آن که باید دقت کنید
پس برای اجرای اولین بارِ حلقه رشته ی



"this year is "


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



"([^%d.+]+)(%d*)"


یعنی بعد از جواب آخرین کاراکتری که در پرانتز اول که همان آخریم کاراکترش space بود ، از این کاراکتر به بعد ، دنبال عدد بگرد. وقتی از این کاراکتر space (کاراکتر شماره ی 13) به بعد دنبال عدد میگردیم ، 2 تا مجموع عدد پیدا میکنیم که یکی جوابش رشته ی "2015" میشود و دیگری (آخری) رشته ی "1394"
اما کدام یک از این اعداد جواب سومین پرانتز مان هست؟
جواب این سئوال در Magic کاراکترِ (علامت) عبارت قبلی هست یعنی چون عبارت یا پرانتز قبلی که Magic کاراکتر اش منفی بود ، یعنی کوتاه ترین جمله و نزدیک ترین جمله به جواب عبارت پرانتز اول را بیای نه بزرگترین را پس جواب پرانتز سوم یعنی d% رشته ی "2015" میشود
خوب جواب سومین پرانتز که رشته "2015" میشود و دقیق از کاراکتر قبل از آن (space) هم که جواب پرانتز یا همان کلوشه ی اولی هست پس در اینجا دومی که (-.) بود جوابش چه میشود؟ پس جوابی برایش پیدا نشد و چون Magic کاراکتر منفی دارد ، اگر هم جوابی برایش پیدا نشود ، ایرادی ندارد بنابراین در خروجی بجای پرانتز دومی رشته ی "" چاپ میشود (دقت کنید که این رشته حاوی space یا فضای خالی نیست هر چند در اجرا شبیه فضای خالی چاپ میشود
پس برای پرانتز اول یا متغییر Content1 ، رشته ی :



"this year is "


و برای پرانتز دوم یا متغییر Content2 رشته ی :



""


و برای پرانتز سوم یا متغییر Content3 رشته ی :



"2015"


برگردانده میشود.
در اجرای بار دوم حلقه هم بعد از کاراکتر "29" جستجو میشود که اگر همین ترتیب را رعایت کنید ، برای پرانتز اول ، رشته ی :



" (christian "


و برای پرانتز دوم رشته ی :



""


و برای پرانتز سوم رشته ی :



"1394"


برگردانده میشود
در سومین اجرای حلقه ، و برای پرانتز اول رشته ی :



" solar)"


و برای دو تا پرانتز دیگر ، مقداری یافت نمیشود و چون الویت کمتری نسبت به پرانتز اول دارند ، پیدا شدن یا نشدن جواب در نتیجه ی پرانتز اول تاثیری ندارد پس مقدار پرانتز دوم و سوم هم میشود :



""



تذکر مهم : بر اساس تجربه ، سعی کنید اگر از کلاس کاراکترِ نقطه برای انتخاب تمامی یا بخشی از کاراکترهای بین دو علامت خاص که استفاده میکنید ، علامتِ Magic کاراکتر را برای کلاس کاراکترِ نقطه ، مثبت در نظر بگیرید نه منفی یا ضرب یعنی (+.)




31)



MainStr = "this year is 2015.11.29 (christian 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1, Content2, Content3 = string.find(MainStr, "([^%d.+]+)(.*)(%d+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2 .."\n"..Content3, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


همان مثال قبلی هست فقط Magic کاراکترهای پرانتز دوم و سوم تغغیر کرد
جواب پرانتز اول که مشخص بود میشود رشته ی :



"this year is "


Magic کاراکترِ پرانتز سوم چون باز الویت دارد ، این پرانتز محتویاتش جستجو میشود یعنی بعد از کاراکتر شماره ی 13 که فضای خالی هست ، برای پرانتز سوم که d% هست یعنی برای عدد جستجو میشود که مثل قبل ، دو عدد "2015" و "1394" یافت میشود
اینکه کدام عدد انتخاب شود ، بستگی یه Magic کاراکتر پرانتز قبلی یا پرانتز دومی دارد که ضرب هست یعنی بزرگترین و دورترین جمله و نمونه انتخاب شود که دورترین عدد نسبت به جواب و رشته ی پرانتز اول ، عدد و رشته ی "1394" هست تا کاراکتر های مابین شان (یعنی کاراکترهای ما بین پرانتز اول که رشته ی "this year is " بود و رشته ی "1394") همان جواب رشته ی دوم شود پس جواب ها به این صورت میشود :

پرانتز اول یا مقدار متغییر Content1 :



"this year is "


پرانتز دوم یا مقدار متغییر Content2 :



"2015.11.29 (christian 139"


پرانتز سوم یا مقدار متغییر Content3 :



"4"


تذکر : اینکه چرا جواب پرانتز سوم یا همان متغییر Content3 ، بجای اینکه کل رشته ی "1394" شود (چون علامت Magic کاراکتر مثبت دارد که یعنی مجموع کاراکتر آن نوع که عدد هست) فقط تک کاراکترِ و رشته ی "4" شد ، دلیل اش را نمیدانم






تمرین (بدون توضیح)

32)



MainStr = "this year is 2015.11.29 (christian 1394 solar)"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1, Content2 = string.find(MainStr, "(.-)(%d-)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


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



تمرین (بدون توضیح)

33)



date = "17.492.03 ^Array.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(.-)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end








Magic کاراکترِ علامت سئوال (علامت سئوال انگلیسی) ? :

آخرین Magic کاراکتر علامت سئوال هست که ماهیت اش کلا با بقیه فرق دارد . اگر Magic کاراکترِ علامت سئوال بعد از یک کاراکتر یا کلاس کاراکتر یا کپچر کلوشه ای یا هر چیز دیگری بیاید ، اولا دیگر نمیشود از Magic کاراکتر دیگری مثلا مثبت و منفی و ضرب استفاده کرد پس کاراکتری که Magic کاراکترِ علامت سئوال میپذیرد ، فقط یک کاراکتر از همان نوع (مثلا عدد) را میتواند برگرداند نه مجموع آنرا و دوما بدین معناست که کاراکتر مورد نظر ، اگر وجود داشته باشد ، گذاشته شود وگرنه وجود هم نداشت ، مهم نیست (مثل الویت بندی Magic کاراکترها مثلا مثل Magic کاراکترِ منفی که پایین ترین الویت را داشت اما فرقش با Magic کاراکترِ منفی در این است که Magic کاراکترِ منفی ، مجموع کاراکتر همان نوع را برمیگرداند ولی Magic کاراکترِ علامت سئوال تنها یک کاراکتر را برمیگرداند)



34)



date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "(%a?%d+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


همانطور که میدانیم ، Magic کاراکترِ علامت سئوال ، کمترین الویت را دارد پس در اجرای هر بار از حلقه ، a% را از ذهن مان پاک میکنیم بنابراین فقط به دنبال مجموع عدد میگردیم و بعد از یافتن عدد ، اگر فقط یک کاراکترِ قبل از این عدد (چون Magic کاراکترِ علامت سئوال فقط تک کاراکتر را برمیگرداند) از نوع حروف بود ، آن هم در جواب گنجانده میشود و اگر نبود هم که مهم نیست پس در بار ال ، رشته ی "17" برگردانده میشود و چون قبل از عدد 1 از آن ، کاراکتری وجود ندارد که بخواهد از نوع حرف باشد یا نه ، پس در دفعه ی اول کلا فقط همین رشته برگردانده میشود
در دفعه ی دوم به بعد هم به ترتیب رشته های 492 و 03 و 28 و 74
در بار ششم ، عدد 25 یافت میشود (بعد از رشته ی "Array") و چون کاراکتر قبل از کاراکتر اول آن (قبل از عدد "2") ، کاراکتری از نوع حروف وجود دارد پس فقط همین یک کاراکترِ قبل از آنرا برمیگرداند که کلا رشته ی "y25" برگردانده میشود
بار هفتم هم به همین ترتیب رشته ی "n5" برگردانده میشود
و میتوانید تا آخر جواب ها را همینطور چک کنید ...



35)



date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([+-]?%d+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end


یکی از کاربردهای Magic کاراکترِ علامت سئوال ، در قضیه ی یافتن علائم قبل از عدد مثل مثبت یا منفی بودن عدد هست :
باز هم مشخص است که در این پترن اگر قبل از عددی ، علامت های مثبت یا منفی در رشته ی اصلی بیایند ، به همراه عدد ، آن علائم را برمیگرداند و اگر هم که نیامده باشد ، فقط عدد را برمیگرداند
پس در اجرای بار اول ، رشته ی "17+" و بار دوم به بعد رشته های 492 و 03 و 28- و .... که آخری هم 365+ را برمیگرداند
این مثال با کد زیر هم تقریبا برابری میکند (بجای علامت سئوال از منفی استفاده شد که البته فرق شان را میدانیم) :



36)



date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
LastChar = 0
var=false
while var==false do
FirstChar, LastChar, Content1 = string.find(date, "([+-]-%d+)",LastChar+1)
if Content1==nil then
var=true
break;
else
Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
end





نکته جدید و مهم :
اگر تابع string.find را در شرطی بکار ببریم که با علامت ^ پترن آن شروع شود ، آن نوع پترن را در ابتدای رشته ی اصلی ، چک میکند مثلا :



37)



date = "17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a"
if string.find(date, "^%d") then
Dialog.Message("Notice", "string begining with digit", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
else
Dialog.Message("Notice", "string Dont begining with digit", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end






تابع string.gsub :
دقت کنید این تابع را با تابع string.sub که برای بریدن رشته بود و توضیح داده شده بود ، اشتباه نگیرید. این تابع برای جایگزین کردن یه رشته بجای رشته یا پترن مورد نظر در رشته ی اصلی بکار میرود
دارای 4 آرگومان و 2 خروجی برای تابع هست
در آرگومان اول ، مثل تابع string.find رشته ی اصلی که برای جستجو برای جایگزین کردن را میدهیم
در دومین آرگومان هم باز هم مثل همان تابع ، رشته ای به عنوان پترن یا حتی رشته ی معمولی ای میدهیم تا اگر این رشته در رشته ی اصلی (آرگومان شماره اول) جستجو و یافت شد ، رشته ای که در آرگومان شماره ی سوم دادیم را جایگزین آن کند
در سومین آرگومان ، رشته و حتی رشته ای به عنوان پترن ای میدهیم که اگر مقدار رشته ی آرگومان دوم در رشته ی آرگومان اول (اصلی) یافت شد ، این رشته ی شماره ی 3 را جایگزین رشته ی شماره ی 2 در رشته ی اصلی (آرگومان اول) کند (در این آرگومان حتی بجای رشته میشود تابع هم نوشت که میتوانید برای آموزش نوشتن تابع در این آرگومان ، به سایت اصلی لوا سر بزنید)
چهارمین آرگومان که اختیاری است ، یک عددی را میدهیم که فقط به تعداد آن اگر مقداری یافت شد ، جایگزین شود نه به تعداد همه ی مقادیری که این تابع یافت کرد یا به اصطلاح Match کرد (به مثال 39 دقت کنید ، متوجه منظور میشوید)

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

*** تذکر مهم : در تابع string.gmatch مثل تابع string.find نیست که هر بار برای یافتن مقدار بعدی ، آنرا در حلقه ای قرار دهیم که شماره ی کاراکترِ آنرا در آرگومان سوم تابع string.find میدادیم . تابع string.gmatch اتوماتیک رشته ی آرگومان دوم را در همه ی کاراکترهای رشته ی آرگومان اول جستجو میکند یعنی بدون استفاده از حلقه ، کار حلقه و آرگومان سوم تابع string.find را انجام میدهد و نتیجه را برمیگرداند



38)



MainStr = "Today is 2015/12/3, firm"
ModifiedStr, MatchNumber = string.gsub(MainStr, "%a+", "word\n")
Dialog.Message("Notice", ModifiedStr.."\n"..MatchNumber , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در خط دوم ، پترن (در آرگومان دوم) را در رشته ی اصلی (آرگومان اول) جستجو میکند و هر گاه مقداری یافت ، آرگومان شماره سوم را جایگزین آن میکند
خوب پترن +a% که مشخص هست . اولین مقداری که پیدا میکند رشته ی "Today" در رشته ی اصلی هست (به مقادیری که پیدا میشود ، Match هم میگویند یعنی الان رشته ی "Today" اولین match ماست). خوب همانطور که گفته شد ، تابع string.gmatch مثل تابع string.find نیست که برای پیدا کردن match ها یا مقادیری که در پترن داده شد ، در حلقه ای قرار دهیم برای اینکه تا آخرین کاراکترِ رشته ی اصلی ، برای جستجو چک کند. تابع string.gmatch اتوماتیک خودش همه ی کاراکترهای رشته ی اصلی را برای جستجو چک میکند پس برای جستجو به سراغ کاراکتر بعدی که space و از آن به بعد هست میرود . بنابراین دومین مقدار (دومین Match) یافت شده ، رشته ی "is" در رشته ی اصلی است و به همین ترتیب ، سومین match آن هم رشته ی "firm" میشود
خوب حالا کار این تابع این هست که سومین آرگومان که رشته ی "word\n" هست را جایگزین این مقادیر یا match هایی که پیدا شد ، قرار میدهد یعنی بجای رشته های "Today" و "is" و "firm" در رشته ی اصلی ، رشته ی "word\n" را قرار میدهد (میدانیم که هر کجا "\n" آمد ، این مقدار چاپ نمیشود یعنی به خط پایین تر میورد) پس ابتدا رشته ی "word" چاپ میشود و به خط بعدی میرود و سپس کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "Today" و "is" بود) و سپس باز هم رشته ی "word" چاپ میشود و به خط بعدی میرود و سپس دوباره کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "is" و "2015/12/3" بود) و سپس رشته ی



"2015/12/3,"


و سپس دوباره کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "2015/12/3," و "firm" بود) و سپس رشته ی "word" چاپ میشود و به خط بعدی میرود و در اولین متغییر تابع string.gsub ذخیره میشود پس در متغییر ModifiedStr رشته ی زیر ذخیره میشود :



"word
word
2015/12/3, word
"


در دومین متغییر این تابع هم تعداد مواردی که یافت شد یا همان تعداد match ها که 3 تا بودند به عنوان عدد ذخیره میشود (به عنوان عدد) (دقت کنید در صورتی که چهارمین آرگومان این تابع داده نشود) پس مقدار MatchNumber مقدار عدد 3 میشود



39)



MainStr = "Today is 2015/12/3, firm"
ModifiedStr, MatchNumber = string.gsub(MainStr, "%a+", "word\n", 2)
Dialog.Message("Notice", ModifiedStr.."\n"..MatchNumber , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همان مثال قبلی هست ولی در خط دوم ، چهارمین آرگومان هم به آن داده شد
در چهارمین آرگومان که الان عدد 2 دادیم ، به این معناست که مقادیر و match هایی که تا دو تای اول فقط پیدا شدند را جایگزین شود و هر چند تا match هایی که بعد از آن پیدا شدند ، کاری نداشته باشد بنابراین فقط برای دو مقدار اولی که یافت شد یعنی بجای دو match اول که یافت شد یعنی فقط بجای رشته های "Today" و "is" رشته ی "word" قرار میدهد نه بجای همه ی match های یافت شده پس به رشته ی "firm" کاری ندارد پس در متغییر ModifiedStr رشته ی زیر ذخیره میشود :



"word
word
2015/12/3, firm"


ذخیره میشود
در متغییر MatchNumber هم تعداد match هایی که جایگزین شدند ذخیره میشود پس آرگومان شماره ی 4 در صورت وجود ، جایگزین آن میشود پس مقدارش 2 میشود

** تذکر مهم : اگر بجای آرگومان چهارم ، مقدار عددی منفی یک -1 را بدهیم ، دیگر هیچ مقداری که یافت هم شده باشد جایگزین آن نمیشود (حتی اگر آرگومان سوم که همان آرگومان بنام replace هست ، مقدار داشته باشد)



Only the registered members can see the link 9435231833e24f0.png (Only the registered members can see the link eab885eb1.png)










Capture پرانتز در تابع string.gmatch :


مفهوم کپچر پرانتز در تابع string.find و بقیه ی توابع (بجز تابع string.gmatch) را میدانیم و کار کردیم ولی مفهوم این کپچر در تابع string.gmatch متفاوت است
در این تابع ، هر گاه در آرگومان pattern ها (آرگومان دوم) کپچرِ پرانتز بیاید و بعد از آن در آرگومان replacment (آرگومان سوم) ، کاراکتر یا علامت % بگذاریم و بعد از این کاراکتر ، عدد بنویسیم ، منظور همان محتوای شماره های پرانتز در آرگومان دوم هست (به تصویر بالا خوب دقت کنید) مثلا در تصویر بالا ، در آرگومان سوم ، وقتی نوشتیم 3% منظورمان محتوای پرانتزِ سوم در آرگومان دوم (که همان d% هست) ، هست. محتوای پرانتز سوم هم طبق تصویر بالا ، رشته ی "1" میشود . از آن طرف هم در آرگومان سوم (replacement) هر رشته ای که آمد جایگزین رشته ی دوم در رشته ی اول میشود پس در خروجی این تابع ، در رشته ی اصلی ، ابتدا %3 که همان رشته ی "1" هست نوشته میشود (اما در رشته ی اصلی ، رشته ی "version" ابتدا نوشته شده هست که میبینیم) و در واقع این روش برای مرتب کردن و جابجا کردن رشته ها بسیار کاربردی هست



40)



str = "version_1"
NewStr = string.gsub(str, "(%a+)(%p)(%d)", "%3%1%2")
Dialog.Message("Notice", NewStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همان مثال تصویر بالاست
همانطور که گفته شد ، در آرگومان سوم ، وقتی نوشتیم 3% منظورمان محتوای پرانتزِ سوم در آرگومان دوم (که همان d% هست) ، هست. محتوای پرانتز سوم هم طبق تصویر بالا ، رشته ی "1" میشود . از آن طرف هم در آرگومان سوم (replacement) هر رشته ای که آمد جایگزین رشته ی دوم در رشته ی اول میشود پس در خروجی این تابع ، در رشته ی اصلی ، ابتدا %3 که همان رشته ی "1" هست نوشته میشود (اما در رشته ی اصلی ، رشته ی "version" ابتدا نوشته شده هست که میبینیم)
بعد از آن در آرگومان سوم نوشتیم 1% که به این معناست که محتوای پرانتزِ اول در آرگومان دوم را بعد از این بنویسد و پرانتز اول که +a% هست ، محتوای آنرا میدانیم که میشود رشته ی "version" پس بعد از نوشته شدن رشته ی "1" ، کاراکترِ بعد از آن ، رشته ی "version" نوشته میشود پس تا به حال ، خروجی این تابع میشود "1version"
بعد از آن هم که 2% آمد یعنی محتوای پرانتزِ دوم در آرگومان دوم که همان رشته ی "_" میشود پس خروجی این تابع (متغییر NewStr) میشود :



"1version_"




41)



Str = "hello Lua"
NewStr = string.gsub(Str, "(.)(.)", "%2%1")
Dialog.Message("Notice", NewStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



%2 در سومین آرگومان یعنی محتوای دومین پرانتز در آرگومان دوم . دومین پرانتز در آرگومان دوم که نقطه هست یعنی یک کاراکتر (چون علامت + ندارد که مجموع شود) و آن هم کاراکتر دوم در رشته ی اصلی چون محتوای اولین پرانتز ، کاراکتر اول در رشته ی اصلی میشود پس محتوای کاراکتر دوم رشته ی "e" میشود پس در این تابع ، اول همین نوشته میشود و محتوای کاراکتر اول رشته ی "h" میشود که بعد از آن نوشته میشود (یعنی کاراکتر اول و دوم جابجا شدند) پس خروجی این تابع تا به حال رشته ی "eh" شد
خوب همانطور که میدانیم ، این تابع ، حتی بدون اینکه در حلقه قرار گیرد ، ادامه ی آنرا هم جستجو و مقادیر آرگومان سوم را اعمال و جایگزین میکند بنابراین به سراغ سومین آرگومان به بعد میرود و همین کار را برای آنها هم انجام میدهد یعنی این بار %2 میشود محتوای چهارمین کاراکتر و %1 میشود محتوای سومین کاراکتر و اعمال و جابجا میشود یعنی کاراکتر سوم و چهارم در رشته ی اصلی ، در خروجی این تابع ، جابجا میشوند ولی چون در این کاراکترها (یعنی سومین و چهارمین کاراکتر در رشته ی اصلی) هر دو تای آنها کاراکتر "l" هستند پس جابجا شدن آنها تاثیری در خروجی ندارد بنابراین نتیجه ی تابع تا به حال میشود "ehll"
حال نوبت به کاراکترهای پنجم و ششم هست که جابجا شوند (یعنی جای کاراکتر "o" و کاراکتر space عوض میشود) بنابراین نتیجه ی تابع تا به حال میشود "ehll o"
کاراکتر هفتم و هشتم هم همینطور پس خروجی تابع میشود "ehll oul"
کاراکتر نهم هم نوشته میشود سر جای خودش چون کاراکتر دهم ای وجود ندارد در رشته ی اصلی که با آن جابجا شود بنابراین خروجی کامل و نهایی این تابع رشته ی "ehll oula" میشود که در متغییر NewStr ذخیره میشود





تابع string.gmatch :

تابع iterator ای هست که (در حلقه ی مورد نظر) ، هر بار ، مقدار و حاصل کاراکتر بعدی پترن را جستجو کرده و باز میگرداند . در واقع کار همان تابع string.find را در حلقه و با آرگومان سوم که مشخص کننده ی شماره ی کاراکتر برای جستجو بود را میکند (برای آشنایی با توابع iterator و نحوه ی استفاده از این توابع درحلقه ها ، میتوانید به مقاله ی جداگانه ای که نوشته شده ، نگاهی بیاندازید)
در آرگومان اول این تابع ، رشته ی اصلی برای جستجو و در آرگومان دوم آن pattern مورد نظر را مینویسیم


42)



Str = "hello world from Lua"
for w in string.gmatch(Str, "%a+%s+%a+") do
Dialog.Message("Notice", w, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


بنابراین این تابع هر بار ، مقدار پترن مورد نظر را جستجو میکند و بار دیگر ، از کاراکتر بعد از کاراکترِ آخری ، شروع به جستجو کردن میکند و نتیجه را هر بار در متغییر w ذخیره میکند بنابراین 2 بار در این مثال این تابع تکرار میشود که بار اول مقدار رشته ی "hello world" و بار دوم رشته ی "from Lua" در متغییر w ذخیره میشود (برای درک درست ، حتما به مقاله ی توابع iterator سر بزنید)


توابع هایی که در توابع قسمت string از pattern ها استفاده میکردند ، تمام شد




تابع string.format :
این تابع عملکردی شبیه به تابع string.gsub دارد و حالت و شکل یک رشته را تغییر میدهد ولی تفاوت آن این است که در آرگومان اول آن ، بجای pattern ها (که pattern ها را در آرگومان دوم string.gsub میدادیم) ، اما این بار pattern به آن نمیدهیم و چیزی بنام format میدهیم . format ها از لحاظ ظاهری شبیه pattern ها هستند ولی در کاراکترها و عملکرد اصلا شبیه آنها عمل نمیکنند بنابراین به این تابع نمیپردازیم فقط یک مثال بدون توضیح گذاشته میشود



43)



d = 5; m = 11; y = 1990
Dialog.Message("Notice", string.format("%03d/%d/%d", d, m, y), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
pi=3.1418
Dialog.Message("Notice", string.format("pi = %2f",pi), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);

SajjadKhati
11-03-16, 12:03
توابع String ها :

Only the registered members can see the link

این آموزش برای قسمت توابع هایی که مربوط به string هاست ، میشود . البته بجز توابع ای از string ها که به pattern ها مربوط میشوند که در آموزش قبلی به آن پرداختیم یعنی در این آموزش به توابع string.find و string.format و string.gmatch و string.gsub و string.match و حتی تابع string.sub که قبلا در بحث pattern ها به آنها پرداختیم ، نمیپردازیم



1) تابع string.dump :
یکی از مهمترین توابع string هاست که تابع را به رشته ی دودویی تبدیل میکند
این تابع ، یک آرگومان آنهم از نوع تابع دریافت میکند (ورودی آن باید از نوع تابع باشد نه چیز دیگری) و به عنوان متغییر خروجی ، رشته ای دودویی یا همان باینری تحویل میدهد (رشته ی معمولی و قابل درک نیست)
** تذکر : خروجی این تابع یعنی رشته ی باینری ، بعدا میتواند توسط تابع loadstring تبدیل به تابع شود (هر چند رشته ی باینری باشد) یعنی این دو تابع ، معکوس هم کار میکند
****تذکر مهم 2) : اگر آرگومان و ورودی رشته ی loadstring هم خودش یک تابع دیگر در آن تعریف شده باشد ، برای مقدار دهی آرگومان هایی که در این رشته هست ، موقع فراخوانی نام تابعی که با تابع loadstring تعریف شد ، باید مستقیما به خود نام تابع loadstring این آرگومان ها را بدهیم تا آرگومان های این تابع را به عنوان آرگومان های تابع داخلی آن ست کند



مثال 1 )



function Func(y,a)
x=(y+a)^2
return x
end

FuncStrBinary = string.dump(Func)
Dialog.Message("Notice", FuncStrBinary, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در آرگومان خط ششم یعنی در آرگومان تابع string.dump ، خوب نمیخواهیم تابع Func را اجرا کنیم پس نباید آرگومان های این تابع که y و a هستند را در این خط فراخوانی کنیم پس فقط ذکر نام تابع کافی است . بنابراین string.dump ، تابع Func را در ورودی گرفته و آنرا بصورت رشته ی باینری در متغییر FuncStrBinary ذخیره میکند لذا در خط هفتم این تابع بصورت رشته ی باینری (نه رشته ی معمولی) چیزی شبیه رشته ی زیر درمیاید :



"<-LuaQ"




2) تابع loadstring :
هر چند این تابع در لیست توابع string ها نیست و در قسمت توابع basic function هاست ولی چون تابع بسیار کاربردی در مبحث رشته هاست ، همین جا توضیح داده میشود
این تابع ، رشته ای (چه رشته ی باینری یا رشته ی معمولی) را به عنوان ورودی دریافت میکند و به عنوان خروجی ، آنرا (رشته ای که به عنوان ورودی دریافت کرده را) در قالب یک تابع ارائه میدهد (یعنی خروجی آن یک تابع هست)
یعنی انگار ورودی آنرا در قالب تابعی که آن نام آن تابع ، همان نام خروجی آن است ، تعریف کردیم برای درک بهتر ، حتما به مثال ها دقت کنید



مثال 2)



Func = loadstring("i=3")
FuncCalled = Func()
Dialog.Message("Notice", i, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


به توضیح خط اول خوب دقت کنید :
الان که در خط اول این مثال که نوشته شد :



Func = loadstring("i=3")


آرگومان آن که رشته ی "i = 3" هست را در قالب تابعی بنام Func (متغییر خروجی آن) تعریف میکند یعنی این آرگومان و رشته را به عنوان بدنه ی تابع Func تعریف میکند . یعنی دقیق این خط معدل آن اسن که بجای این خط بنویسیم :



function Func()
i = 3
end


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



function Func()
i = 3
end

FuncCalled = Func()
Dialog.Message("Notice", i, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همانطور که میبینید ، خط دوم و سوم آن مثل مثال شماره 2 هست
همانطور که گفته شد ، خط اول در مثال شماره ی 2 یعنی



Func = loadstring("i=3")


یا 3 خط اول در کد بالا یعنی



function Func()
i = 3
end


، بدین معناست که در هر دو جا ، تابع تعریف شد ولی فراخوانی نشد (همانطور که میدانیم برای اجرای تابع ، باید ابتدا فراخوانی شود) بنابراین برای اجرای این توابع نیاز هست که فراخوانی شود که در خط دوم از مثال دوم (یا همان خط پنجم از کد بالا که شبیه هم هستند) ، تابع Func را فراخوانی کردیم (همانطور که میدانیم علامت فراخوانی تابع ، پرانتز است که جلوی Func در این خط میبینید) پس با فراخوانی تابع Func ، متغییر i (که مقدارش برابر 3 هست) ، هم که در بدنه ی این تابع وجود داشت ، تعریف شد و آنرا در متغییر FuncCalled قرار دادیم (دقت کنید که چون تابع Fun هیچ مقداری را برنمیگرداند پس متغییر FuncCalled هم حاوی مقداری نیست)
در خط بعدی یعنی در خط آخر هم مقدار متغییر i را که با فراخوانی تابع Func تعریف شد ، چاپ میکنیم



مثال 3)



t={}
loadstring("t[1]=2")()
Dialog.Message("Notice", t[1], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


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



(function () t[1]=2 end)()


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



مثال 4)



i=4
x= i^2
Func = loadstring("return "..x)
FuncCalled = Func()
Dialog.Message("Notice", FuncCalled, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


همانطور که میدانید ، خط سوم برابر است با تابع زیر :



function Func()
return x
end


دقت کنید که در خط سوم مثال 4 یعنی در این خط :



Func = loadstring("return "..x)


بعد از نوشتن گزینه ی return به عنوان رشته ، بعد از آن حتما یک space بگذارید . بعد رشته را بسته و بعد هم متغییر x را به آن بچسبانید . اگر آن کاراکتر space را نگذارید و به صورت رشته ی :



"return"..x


بنویسید مثل آن میماند که کد زیر هم همین کار را کرده و بنویسید :



function Func()
returnx
end


که مشخص است آنرا به عنوان کلمه ی کلیدی return نشناخته و به عنوان یک متغییر میشناسد در تابع
در خط چهارم در مثال 4 ، هم همانطور که میدانیم ، تابع Func را اجرا کرده و مقدارش را در متغییر FuncCalled میریزیم و چون تابع Func مقداری را برمیگرداند (مقدار x را) پس همان مقدار در متغییر FuncCalled ذخیره میشود و در خط آخر هم فراخوانی میشود



مثال 5)



loadstring("d=99")()
loadstring([[Dialog.Message("Notice", d, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);]])()


در خط اول ، تابع loadstring ، تابع بی نام ای را تعریف و آنرا اجرا میکند ، بنابراین متغییر d با مقدار 99 تعریف و مقداردهی میشود
در خط بعدی هم شبیه همین ساختار است و تابعی است که مقدار متغییر d را که قبلا تعریف شده بود را چاپ میکند منتها بخاطر نوع سینتک تابع Dialog.Message نمیشود آنرا در رشته ای که سینتکس " " دارد تعریف کرد پس آنرا در رشته ای که سینتکس [[ ]] دارد تعریف میکنیم (همانطور که میدانید رشته ها را در دو سینتکس " " و [[ ]] میشود تعریف کرد که دومی سینتکس کامل تر و قدرتمندتر و هم برای رشته های یک خطی و چند خطی استفاده میشود)



مثال 6)



function Func(y,a)
x=(y+a)^2
return x
end

FuncStrBinary = string.dump(Func)
FuncOfFunction = loadstring(FuncStrBinary)
FuncVariable = FuncOfFunction(5,3)
Dialog.Message("Notice", FuncVariable, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تابع string.dump در خط سوم ، تابع Func را به رشته ی باینری تبدیل میکند
تابع loadstring در خط بعدی ، رشته ی باینری FuncStrBinary را در قالب تابع FuncOfFunction تعریف میکند (همانطور که گفته شد ، آرگومان تابع loadstring فرقی نمیکند که رشته ی معمولی باشد یا رشته ی باینری)
همانطور که قبلا گفته شد ، موقع فراخوانی نام تابعی که با تابع loadstring تعریف شد (یعنی موقع فراخوانی تابع FuncOfFunction که در خط هشتم یعنی یک خط مانده به خط آخر انجام گرفت) اگر آرگومان و ورودی رشته ی loadstring (یعنی رشته ی FuncStrBinary در خط هفتم) هم خودش یک تابع دیگر در آن تعریف شده باشد (رشته ی FuncStrBinary که حاوی تابع Func هست) ، برای مقدار دهی آرگومان هایی که در این رشته هست (برای مقدار دادن آرگومان های تابع Func) ، باید مستقیما به خود نام تابع loadstring (یعنی تابع FuncOfFunction) این آرگومان ها را بدهیم تا آرگومان های این تابع را به عنوان آرگومان های تابع داخلی آن ست کند
لذا هر چند تابع FuncOfFunction آرگومانی ندارد ، ولی برای مقداردهی آرگومان داخلی آن باید به همین تابع ، این آرگومان ها را نسبت داد تا آرگومان های تابع داخلی مقداردهی شود پس در خط مقادیر دلخواه آرگومان های تابع FuncOfFunction را میدهیم (که همان آرگومان های تابع Func هست) و چون تابع ، مقداری را برمیگرداند لذا آن مقدار در متغییر FuncVariable ذخیره میشود (در این مثال مقدار FuncVariable برابر 64 میشود) و در خط آخر چاپ میشود
این مثال چون بصورت رشته ی باینری هست ، نمیشود مثل مثال های قبل ، تابع loadstring اش را بصورت تابع جداگانه نوشت



3) تابع tostring :
این تابع ، هر داده ای را به عنوان ورودی دریافت و بصورت رشته بازمیگرداند (البته توابع را بصورت عدد که در تابع string.format استفاده میشود ، برمیگرداند)



مثال 7)



Bool = true
FuncStr = tostring(Bool)
Dialog.Message("Notice", FuncStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


متغییر Bool را که مقداری بولین دارد ، بصورت رشته بازمیگرداند



بقیه ی توابع قسمت string در Autoplay Media Studio معادل دارند و فقط نام معادل آن ذکر میشود و توضیح ای ارائن نمیشود که عبارتند از :
تابع string.byte که معادل تابع String.Asc در AMS هست
تابع string.char که معادل تابع String.Char در AMS هست
تابع string.len که معادل تابع String.Length در AMS هست
تابع string.lower که معادل تابع String.Lower در AMS هست
تابع string.rep که معادل تابع String.Repeat در AMS هست
تابع string.reverse هم رشته را معکوس میکند و تقریبا معادل با تابع String.ReverseFind در AMS هست
تابع string.upper که معادل تابع String.Upper در AMS هست

SajjadKhati
11-03-16, 12:26
نکاتی درباره توابع در لوا :

1) تابع با چندین مقدار بازگشتی :
یک تابع میتواند چندین مقدار بازگشتی را بازگرداند. در این صورت باید به تعداد مقدار بازگشتی آن تابع (مثلا فرض میکنیم تابع مان 3 مقدار بازگشتی دارد)، برایش متغییر تعریف کرد و همه ی متغییرها را با ویرگول از هم جدا کرد . مثلا در مثال زیر :



function func(a,b)
Val1 = a+b
Val2=a*b
Val3 = a-b
return Val1,Val2,Val3
end


Plus,multiplication,Minus = func(200,100)
Dialog.Message("Notice", "a+b = "..Plus.."\na*b = "..multiplication.."\na-b = "..Minus, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تابع بالا که اسمش func هست ، 3 مقدار Val1 و Val2 و Val3 را برمیگرداند . موقع فراخوانی ، هر کدام به ترتیب داخل متغییرهای مربوط به خودشان (که موقع فراخوانی تابع func از آن استفاده کردیم) ذخیره میشوند. یعنی مقدار بازگشتی Val1 (که در مثال بالا مقدارش 300 هست) داخل متغییر Plus و مقدار Val2 (که مقدارش 20000 هست) داخل متغییر multiplication و مقدار Val3 (که مقدارش 100 هست) داخل متغییر Minus ذخیره میشوند
یعنی اولین مقدار بازگشتی ، داخل الین متغییر و دومین مقدار بازگشتی داخل دومین متغییر و ... ذخیره شده است
توجه کنید که اگر مثلا در فراخوانی تابعی که 3 مقدار بازگشتی دارد ، فقط 2 متغییر به آن تابع اختصاص دهیم ، در واقع دو مقدار بازگشتی را فقط به آن تابع اختصاص دادیم و مقدار بازگشتی سوم ، بلا استفاده میماند . یا مثلا اگر این تابع را بدون اختصاص دادن متغییر فراخوانی کردیم ، فقط مقدار بازگشتی اول را برمیگرداند یعنی در مثال بالا اگر این طور بنویسیم:



function func(a,b)
Val1 = a+b
Val2=a*b
Val3 = a-b
return Val1,Val2,Val3
end

Dialog.Message("Notice", func(200,100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


تابع func ، فقط مقدار اول که Val1 بود (که مقدارش 300 میشد) را برمیگرداند

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



Dialog.Message("Notice", Dialog.Message("Notice", "Your message here.", MB_YESNO, MB_ICONINFORMATION, MB_DEFBUTTON1), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


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



function first(arg1)
arg1=arg1^2
return arg1
end

function secand(arg2,arg3)
new=arg2/arg3
return new
end

NewNum = secand(first(6),9)
Dialog.Message("Notice", NewNum, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



3) اجرای تابع داخل تابع دیگر :
هر وقت تابعی داخل تابع دیگر باشد ، تابع داخلی آن ، مثل توابع دیگر تا از تابع خارجی آن فراخوانی نشود ، اجرا نمیشود . مثل :



function Init(a,b,c)
function Sec(a,b)
ABC = a+b+5
return ABC
end
return Sec(a,b)
end


Nm = Init(10,20,30)
Dialog.Message("Notice", Nm, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در مثال بالا ، تا زمانی که تابع داخلی آن (تابع Sec) از تابع خارجی آن (تابع Init) فراخوانی نشود ، تابع Sec اجرا نمیشود .
باید توجه داشت که بصورت مستقیم نمیتوانیم تابع داخلی یک تابع را فراخوانی کنیم چون تابع اجرا نشده و تا تابعی اجرا نشود (و کلا هر مقداری در لوا تعریف نشود) ، مقدارش nil است یعنی در مثال بالا نمی توانیم این جوری فراخوانیکنیم :



function Init(a,b,c)
function Sec(a,b)
ABC = a+b+5
return ABC
end
return Sec(a,b)
end


Nm = Sec(10,20)
Dialog.Message("Notice", Nm, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


4) بدست آوردن تعداد مقادیر بازگشتی یک تابع (select) :
برای درک تعداد مقادیر بازگشتی یک تابع ، میتوانید از تابع select (تابع اصلی لوا) کمک بگیرید
اولین آرگومان (ورودی) این تابع شماره ی متغییر (آرگومان) ای را میدهیم که در آرگومانهای بعدی این تابع نوشتیم . مثلا در مثال زیر :



value = select(2,"hello","word",67,"string",87)
Dialog.Message("Notice", value, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


چون در آرگومان اول ، عدد 2 آورده شد ، یعنی دومین آرگومان بعد از خودش (به غیر از خودش) را برگردان (یعنی سومین آرگومان) که میشود رشته ی word
در مثال زیر :



function func(first,secand)
first=first+1
secand=secand-1
return first,secand
end

Dialog.Message("Notice", select(2,func(5,2)), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


چون تابع func ، دو مقدار را برمیگرداند ، پس 2 مقدار در دو آرگومان تابع select ذخیره میشود (اولیین آرگومان تابع select که عدد 2 هست و دومین آرگومان که همان اولین مقدار بازگشتی تابع func (مقدار first) و سومین آرگومان هم برابر دومین مقدار بازگشتی تابع func (مقدار secand) است) پس معلوم است که خروجی تابع select ، یکی بعد از اولین آرگومان(که 2 است) یعنی سومین آرگومان تابع select که مقدار secand (که مقدارش 1 هست) میباشد
مثال بالا برابر است با مثال زیر :



function func(first,secand)
first=first+1
secand=secand-1
return first,secand
end

One,Two = func(5,2)
Dialog.Message("Notice", select(2,One,Two), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


اگر بجای اولین تابع select ، بجای شماره (عدد) ، رشته ی "#" را قرار دهیم ، تعداد آرگومان هایی که در آن وجود دارد را باز میگرداند . پس برای بدست آوردن تعداد مقادیر بازگشتی یک تابع ، کافیست در اولین آرگومان تابع select ، رشته ی "#" و در دومین آرگومانش ، تابع مورد نظر را فراخوانی کنیم . پس برای بدیت آوردن تعداد بازگشتی تابع func در مثال اول ، کافیست :



function func(a,b)
Val1 = a+b
Val2=a*b
Val3 = a-b
return Val1,Val2,Val3
end

Dialog.Message("Notice", select("#",func(5,2)), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


نکته : برای متوجه شدن تعداد مقادیر بازگشتی یک تابع ، از این روش استفاده میکنیم ولی برای متوجه شدن تعداد یک آرایه این تابع کارایی ندارد . برای این منظور کافیست علامت # (دقت کنید که این بار رشته نیست) را کنار اسم آرایه بگذارید (اما دقت کنید که این روش برای آرایه ها،در مواردی که عضو آرایه ها بصورت عدد نباشند (بصورت index نباشند) و با حروف تعریف شوند ، جواب نمیدهد که در این صورت باید از تابع Table.Count از AMS استفاده کنید وگرنه در لوا باید از تابع pairs در iterator ها (در مقاله های بعد ، آموزش ایتریتورها گفته خواهد شد) ، استفاده کنید)


5) استفاده از علامت آندرلاین _ برای متغییرهای بلا استفاده :
گاهی داخل بعضی از الگوریتم ها ، برای توابعی که چندین مقدار بازگشتی دارند ، از علامت آندرلاین _ برای آن متغییر استفاده میشود (یا با همان تابع select میتوان بهتر مدیریت کرد) . هر چند این متغییر هم مثل متغییرهای عادی قابل استفاده هست و فرقی ندارد که بجای آن ، از اسمی دیگر استفاده کرد :



_,s = string.find("This is Lua in", "Lua")
Dialog.Message("Notice", s, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


که در اینجا متغییر اول تابع string.find ، که با _ مشخص شد ، استفاده نمیکنند (البته میشود استفاده هم کرد مثل بقیه ی متغییرها)


6) تابع با ورودی و آرگومان متفاوت :
برای تعریف تابع (و هر آرگومان دیگر) که تعداد آرگومان هایش از قبل مشخص نباشند ، از علامت سه تا نقطه ... استفاده میشود . بعد ورودی ها را داخل آرایه میریزیم و بعد با بدست آوردن تعداد آن (که با علامت # مشخص میشد) ، به الگوریتم مان میپردازیم :



function average(...)
result = 0
local arg={...}
for i,v in pairs(arg) do
result = result + v
end
return result/#arg
end
برای فراخوانی و استفاده هم :
Debug.Print(average(1,4,3,5,4).."\r\n"); --result = 3.5
Debug.Print(average(1,4,5,4,4,3,7,8,9,4,2,9).."\r\n"); --result = 5
Debug.Print(average(2,9)); --result = 5.5
Debug.ShowWindow(true);


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



(function ()

end)()
مثلا :
(function (a,b)
a=a*a
Dialog.Message("Notice", a, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end)(6,7)



8) تبدیل رشته به تابع (loadstring) :
تابع loadstring که جزء توابع اصلی لواست ، رشته را به عنوان ورودی دریافت و به تابع قابل اجرا تبدیل میکند (البته اگر رشته ها طبق قواعد لوا یا AMS تعریف شده باشند)
چون مقدار بازگشتی اش تابع هست ، پس برای اجرای آن مثل همه ی توابع ، آخرش باید علامت تابع (که در نکته ی 7 گفته شد دو علامت پرانتز هست) گذاشته شود
نکته ی جالب توجه اینکه بعد از اجرای این تابع ، مقدار بازگشتی اش (مقدار بازگشتی loadstring بعد از اجرا) ، nil میشود
نکته ی بعد اینکه در لوا برای تعریف رشته هم از علامت " " و هم از علامت ' ' و هم از علامت [[ ]] استفاده میشود
در مثال زیر ، قبل از اجرا شدن تابع loadstring ، میتوانید نوع بازگشتی آنرا که تابع هست ، ببینید :



Qw=[[
a=5
c=a*4
Dialog.Message("Notice", "in anonymous function!\n"..c, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
]]

x=loadstring(Qw)
Dialog.Message("Notice", type(x), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در مثال زیر هم (که با گذاشتن علامت دو پرانتز ، آخر تابع loadstring به منظور اجرای تابع بازگشتی آن) ، رشته ی اصلی را اجرا کنید و بعد ، نوع بازگشتی را که قبلا اجرا function یا تابع بود و بعد از اجرا که nil میشود را ببینید :



Qw=[[
a=5
c=a*4
Dialog.Message("Notice", "in anonymous function!\n"..c, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
]]

x=loadstring(Qw)()
Dialog.Message("Notice", type(x), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



9) استفاده از حلقه در تابع :
اگر در تابعی ، حلقه ای بکار رود که داخل آن حلقه ، مقدار بازگشتی (return) برای تابع لحاظ شود ، آن تابع دیگر تکرار نمیشود با آنکه حلقه ی تکرار در آن وجود دارد مثل :



function DontRept()
for i=1,5 do
Dialog.Message("Notice", i, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
return i
end
end

DontRept()


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



function Rept()
for i=1,5 do
Dialog.Message("Notice", i, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end
return i
end

Rept()


10) فراخوانی تابع با تک آرگومان آرایه ای :
اگر تابعی تعریف کنیم که فقط یک آرگومان آن هم از نوع آرایه داشته باشد (نه چندین آرگومان) ، میتوانیم علامت تابع یعنی پرانتز را هنگام فراخوانی نگذاریم یعنی :



function Ty(Arr1)
return Arr1[2]
end

Dialog.Message("Notice", Ty{560,"salam"}, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


11) ایجاد و فراخوانی آرایه به روشی دیگر :
اگر آرایه ای داشته باشیم که key (یا به اصطلاح سمت چپ تساوی آن ، در متغییری ذخیره شده باشد) ، مثل متغییر foo و x و y در آرایه ی زیر :



t={foo="bar",x=5,y=true}


به 2 حالت میشود آنرا فراخوانی کرد که اول به این گونه:



t.foo
t.x
t.y


برابر است با نوع دوم اش به این گونه :



t["foo"]
t["x"]
t["y"]


برای ایجاد آرایه هم میشود key ها را بصورت در هم شماره بندی و مقدار دهی کرد یعنی :



t={[3]=12,[7]="salam",[1]="word",[10]=653}
Dialog.Message("Notice", t[1].."\n"..t[3].."\n"..t[7].."\n"..t[10], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


که [1]t برابر رشته ی "word" و [3]t برابر 12 و [7]t برابر رشته ی "salam" و [10]t برابر 653 هست . توجه کنیم که در این حالت بقیه ی مقادیر nil هست مثل [2]t و ... و هم اینکه نباید در این حالت که همه ی key ها شماره گذاری شده اند ، بعضی از آنها را شماره گذاری نکنیم مثلا این طور صحیح نیست :



t={[3]=12,"salam",[1]="word",653

SajjadKhati
11-03-16, 12:37
توابع Iterator در لوا :

iterator به معنای لغوی تکرار کننده هست. در واقع در لوا ، ایتریتور به تابعی میگویند که با استفاده از هر نوع حلقه ای (مثل for و while و repeat که در بین این ها for رایج هست) ، آن تابع تکرار میشود تا زمانی که اولین مقدار بازگشتی آن تابع nil شود
ساختار کلی iterator ها به شکل زیر است :
Only the registered members can see the link (Only the registered members can see the link)

ایتریتورها میتوانند بصورت مستقیم از حلقه ها فراخوانی شوند (حلقه ی for) یا بصورت غیر مستقیم که در این صورت از کلمه ی کلیدی return استفاده میشود .
قبل از همه اول به مقدمه میپردازیم
همانطور که در موضوعات قبل بیان شد ، iterator ، نام یک تابع است و تابع iterator بالا که نامش FuncName هست ، 2 مقدار ورودی (آرگومان first و secand) و 2 مقدار خروجی (مقادیر out_1 و out_2) دارد که برای فراخوانی و استفاده از خروجی و مقادیر آن در حالت معمول و عادی به این روش است (مثلا فرض میکنیم نوع آرگومان ها ، رشته اند) :



inp_1,inp_2=FuncName("Word","Hello")


که در اینجا مقدار out_1 داخل متغییر inp_1 و مقدار out_2 داخل متغییر inp_2 ذخیره میشود
کلمه ی کلیدی 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)


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


ابتدا در خط 8 ، تابع ایتریتور square با مقادیر iteratorMaxCount=3 (که تا آخر ، موقع فراخوانی مجدد تابع ایتریتور ، هر بار این مقدار ثابت میماند ولو حتی متغییر iteratorMaxCount در تابع ایتریتور یا هر کجای دیگر تغییر کند) و currentNumber=0 مقدار دهی میشود و تابع ایتریتور اجرا میشود . چون داخل تابع ایتریتور شرط بکار رفته ، پس طبق نکاتی که گفته شد ، اگر شرط نقض شود ، دیگر حلقه ی for اجرا نمیشود. شرط تابع که کاملا واضح و نیاز به توضیح ندارد .
تابع ، مقدار 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 تاست :



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


3)



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


در خط 12 که فراخوانی شد تابع ایتریتور در حلقه ، مقدار دهی انجام شد (first=0 که همیشه ثابت است در تکرار و اجرای دفعات بعد این تابع و secand=10) . همانطور که گفته شد ، در تابع ایتریتور (و هم هرحلقه ی تکراری) اگر متغییر محلی تعریف شود ، تا آخر اجرایش ، مثل متغییر سراسری ، مقدار قبل اش در آن حفظ میشود اما از بلاک خود خارج نمیشوند یعنی متغییر محلی ای که در تابع تعریف شد (اگر قبلا با همین نام ، متغییری نساخته باشیم) ، مقدارش در بلاک دیگر (مثل بالا که حلقه ی for است) ، nil میشود و برعکس . پس در مثال بالا نمیشود متغییر minuse را در حلقه ی for و متغییر multiple را در تابع ایتریتور فراخوانی کرد چون مقدارشان nil میشود
خوب در تابع ایتریتور ، شرط پابرجاست پس مقدار 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)



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


همانطور که در موضوعات قبل گفته شد ، در خط سوم ، علامت # تعداد آرایه ی t را در n ذخیره میکند که 3 است
در مثال بالا ، همونطور که از فراخوانی خط 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)



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


حالا اگر همان مثال ، کل تابع list_iter رو تابع ایتریتور در نظر بگیریم (در فراخوانی حلقه ی for در خط 13 ، از ویرگول استفاده کنیم) چه اتفاقی رخ میدهد؟ (با کمی تغییرات)
ابتدا در خط 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 ، این کد را جایگزین میکردیم :



Dialog.Message("Notice", type(element), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


چون تابع بازگشتی (تابع بی نام) اجرا نمیشد ، پس مقدار i افزایش نمیافت و i همیشه برابر 0 می بود در تکرار دفعه ی بعد پس تابع تا ابد تکرار میشد
اگر آرگومان تابع ایتریتور list_iter در بالا ، 2 آرگومان میداشت و مقدار هر دو آرگومان مثل هم بودند ، بدیهیست که طبق نکاتی که گفته شد ، در اجرای بار دوم به بعد ، مقدار آرگومان دوم ، همان تابع بازگشتی بی نام است . مثلا به این گونه باشد (که چون دفعه ی دوم ، مقدار h ، تابع است در خط دوم ، دفعه ی دوم ، ارور میدهد) :



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


3)



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


در خط 13 در حلقه ، تابع Init با ویرگول نشانه گذاری شد پس یعنی کل تابع Init به عنوان تابعایتریتور است
همانطور که قبلا هم آشنا شدید با قضایا ، مختصرا توضیح داده میشود . a=10 و b=20 به تابع ایتریتور ارسال میشود. در خط 10 ، تابع داخلی اش فراخوانی میشود (اجرا میشود) . که در نتیجه مقدار ABC=35 و a=9 و b=21 میشود و به ترتیب این مقادیر به متغییرهای One و Two و Three ارسال و در آنها ذخیره میشوند . در اجرای بعد ، آرگوما دوم برابر اولین خروجی تابع (One) میشود پس a=10 و b=35 میشود . دوباره در خط 10 تابع داخلی با مقادیر داده شده به عنوان آرگومان، اجرا میشود و این ماجرا تا وقتی که شرط تابع داخلی نقض نشود ، ادامه دارد
همین مثال را میشود بصورت زیر نوشت :



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


4)



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 استفاده شده باشد . آرایه ی زیر را در نظر بگیرید :



array={x=54,a="hello",7590,y="word"}


به x و a و y در آرایه ی بالا ، key میگویند و 54 و "hello" و "word" مقادیرشان هستند
بدست آوردن key ها یعنی x و a و y بصورت عادی بدون استفاده از توابع next و pairs حداقل میشود گفت کار بسیار سخت و یا نشدنی ایست
این توابع ، وقتی به عنوان تابع ایتریتور استفاده شوند (یعنی در حلقه ی for) ، همه ی مقادیر و نام عنصرها (key ها)ی آن آرایه را برمیگردانند تا به عضو nil یا پوچ یا تعریف نشده برسند که با برگرداندن nil ، حلقه از اجرا بازمیایستد

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



t={32,28,65}
x,y=next(t,2)
Dialog.Message("Notice", x.."\n"..y, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در خط دوم ، اولین آرگومان تابع next را اسم آرایه ی مورد نظرمان که t است را میدهیم . حالا چون در دومین آرگومان اش عدد 2 گذاشتیم پس در اولین مقدار بازگشتی اش که x باشد ، شماره ی عضو بعدی 2 که 3 است (یعنی سومین key تابع t که 3 است) را برمیگرداند و در دومین مقدار بازگشتی اش که y باشد ، مقدار عضو بعدی 2 (یعنی مقدار عضو سوم از آرایه ی t) که 65 است را برمیگرداند
دومین آرگومان تابع next دلخواهی است (اگر در توضیحات سایت اصلی این تابع next هم دقت کنید در دومین آرگومان اش علامت کلوشه [ ] گذاشته است که این علامت یعنی آن آرگومان دلخواهی است گذاشتن و نگذاشتن اش) و اگر این آرگومان را مقداردهی نکنیم ، برابر nil بصورت پیش فرض میشود و اگر nil شود ، اولین شماره ی عضو (key) و اولین مقدار آرایه را برمیگرداند
حالا تابع next به عنوان ایتریتور :



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


در مثال بالا ، در خط دوم ، چون با ویرگول فراخوانی شد تابع next پس نشان دهنده ی این است که این تابع هر چه که هست ، کل این تابع هر بار تکرار میشود . آرگومان اولش اش که معلوم بود و آرگومان دوم اش چون دلخواهی بود و فرض بر این است که از اولین عضو آرایه فراخوانی کنیم ، پس با قرار ندادن (که همان nil قرار دادن آرگومان دوم است) ، از اولین عضو آرایه فراخوانی میشود . k و v هم مثل مثال بالا مقادیر بازگشتی است که توضیح داده شد

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


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

SajjadKhati
11-03-16, 12:40
File I/O در لوا :

همونطور که مشخص است ، این عبارت ، مخفف File Input Output به معنای ورودی و خروجی فایل هست و توابع این بخش دست ما رو برای خواندن از یک فایل یا نوشتن روی آن باز میگذارد
اما قبل از پرداختن به موضوع File I/O ، درباره ی عملگر : (عملگر کلون یا colon) در لوا میپردازیم که یکی از کاربردهای آن در شی گرایی در لواست و یک بخش کوچچکی از شی گرایی در لوا را که به این عملگر مربوط است توضیح میدهیم :
به این کد دقت کنید :



Account = {balance = 0,x=3}


function Account.withdraw(v)
Account.balance = Account.balance - v
return Account.balance
end


a = Account;
Account = nil
Dialog.Message("Notice", a.withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



اگر ساختار بالا برایتان گنگ هست ، پس حتما اول مبحث ماژول ها را مطالعه بفرمایید و بعد به این مبحث وارد شوید (چون تا آخر یکی از بحث ها همین ماژول و فراخوانی آن هست در File IO) ولی به اختصار ، ماژول ، به عضوی از آرایه که بصورت تابع یا متغییر تعریف میشود ، میگویند و فراخوانی آن با علامت دات (نقطه) ما بین اسم آرایه و اسم تابع یا متغییر هست. تابع بالا را هم به شکل زیر هم میشود نوشت و فرقی با هم ندارند :



Account = {balance = 0,x=3,withdraw=function (v)
Account.balance = Account.balance - v
return Account.balance
end}


a = Account;
Account = nil
Dialog.Message("Notice", a.withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


اگر در هر ماژولی ، نام آرایه ی اصلی آن (منظور عضوهای داخلی نیست. منظور در مثال بالا ، بصورت مستقیم ، آرایه ی Account است نه عضوهای آن) برابر پوچ یا nil قرار داده شود ، هر کجا برای فراخوانی ، نام آرایه ای که برابر nil قرار گرفت فراخوانی شود (در کد بالا همان نام آرایه ی Account) ، چون مقدارش پوچ است پس ارور میدهد و اعضای آن فراخوانی نمیشوند به این ترتیب ، در کد بالا :
اولا دیگر نمیشود عضوهای آن (متغییرهای balance و x) بصورت مستقیم در دسترس باشند مگر اینکه قبلا نام آن آرایه رو در متغییر دیگری ریخته باشیم . پس در کد بالا چون در خط 9 ، چون آرایه ی اصلی آنرا که Account بود را برابر nil گرفتیم بنابراین از آن خط به بعد هیچ گاه نمیتوانیم عضوهای آن آرایه را این گونه بصورت مستقیم فراخوانی کنیم و بنویسیم :



Account.x
Account.balance
Account.withdraw(100)


چون این نوع فراخوانی مثل این است که آرایه ای را تعریف نکرده ، عضوهای آن را فراخوانی کنیم
و اگر در خط 8 ، آرایه ی Account را در متغییر دیگری نمیریختیم ، دیگر نمیشد از عضوهایش استفاده کرد و از این به بعد همانطور که مشخص هست ، باید اعضای این آرایه را بصورت غیر مستقیم فراخواند و چون آرایه ی Account را در متغییر a ریختیم ، از این به بعد عضوهای این آرایه بصورت غیر مستقیم فقط با فراخوانی متغییر a در دسترس اند یعنی باید این گونه فراخوانی کنیم :



a.x
a.balance


خوب حالا موقع فراخوانی تابع میرسد . میبینیم که با فراخوانی :



a.withdraw(100)


با وجود اینکه بصورت غیر مستقیم تابع مان فراخوانی شد ، باز هم ارور میدهد و تابع فراخوانی نمیشود . چرا؟
اگر نگاهی به بدنه ی تابع مان (منظور سر یا هدر تابع نیست) بیاندازید ، میبینید که در بدنه ی تابع ، آرایه ی Account فراخوانی شد و چون این مقدار را برابر nil کرده بودیم ، پس مشخص هست به این دلیل است که ارور میدهد همانطور که برای فراخوانی عضوهای قبلی این آرایه ، وقتی بصورت مستقیم فراخوانی میکردیم ، ارور میداد.
اما چاره چیست؟
در این گونه مواقع همانطور که قبلا بجای آرایه ی Account ، متغییری که آرایه ی Account را که در آن ریخته بودیم (متغییر a) را جایگزین اش میکردیم یک ورودی یا پارامتری برای این تابع ساخته و در بدنه ی این تابع هم مثل قبل ، جایگزین آن میکنیم (به هدر تابع که Account.withdraw هست فقط کاری نداریم) یعنی این گونه کد را اصلاح میکنیم :



Account = {balance = 0,x=3}


function Account.withdraw(ArrayPar,v)
ArrayPar.balance = ArrayPar.balance - v
return ArrayPar.balance
end


a = Account;
Account = nil
Dialog.Message("Notice", a.withdraw(a,100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


یعنی چون در بدنه ی تابع مان Account آمد و این آرایه هم پوچ بود ، باید این Account را در بدنه ی اصلی تابع ، جایگزین میکردیم و دقیقا مثل قبل جایگزین آن متغییری که آن آرایه را در آن ذخیره کرده بودیم پس باید در فراخوانی، در پارامتر به عنوان ورودی ، مثل قبل ، همان متغییر a را که محتوای آرایه ی Account در آن ذخیره بود را بفرستیم تا بجای Account ، این پارامتر را جایگزین آن کنیم
پس در فراخوانی :



a.withdraw(a,100)


بجای Account ، متغییر a را به عنوان اولین آرگومان که جایگزین اولین آرگومان تابع که ArrayPar هست ، فرستادیم برای آنکه در بدنه ی تابع اصلی ، آرایه ی Account بصورت مستقیم فراخوانی نشود
آرگومان ArrayPar در تابع بالا ، معروف به آرگومان self یا this هستند و مظهر و نشانه ی این هستند که بجای هر عضوی از آرایه یا حتی خود آرایه (مثل مثال بالا) که nil شد ، به عنوان پارامتر جایگزین در بدنه ی تابع ، مشکل اختلال تابع را حل کنند
خوب اما نقش عملگر کلون : (colon) چیست؟
هر گاه در تابعی به عنوان ماژول (تابع در آرایه) مثل کد بالا ، به هر دلیل (دلیل بالا ، nil شدن مقدار Account بود) نیاز شد که پارامتری را به عنوان نام آرایه (یا ماژول ...) را بصورت مخفیانه بفرستیم ، از علامت کلون یعنی این علامت : استفاده میکنیم. مثلا بجای فراخوانی تابع بصورت :



a.withdraw(a,100)


به این صورت فراخوانی کنیم :



a:withdraw(100)


یا بجای نوشتن هدر تابع (به هدر تابع دقت کنید نه به بدنه اش) ، به این صورت :



function Account.withdraw(ArrayPar,v)
ArrayPar.balance = ArrayPar.balance - v
return ArrayPar.balance
end


این گونه بنویسیم :



function Account:withdraw(v)
self.balance = self.balance - v
return self.balance
end


یعنی کد بالا را میتوانیم به این صورت بنویسیم :



Account = {balance = 0,x=3}


function Account.withdraw(ArrayPar,v)
ArrayPar.balance = ArrayPar.balance - v
return ArrayPar.balance
end


a = Account;
Account = nil
Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


به فراخوانی تابع a:withdraw(100) در خط آخر دقت کنید که همان فرقی با فراخوانی a.withdraw(a,100) ندارد
به عبارتی دیگر هر گاه خواستیم سمت چپ عملگر : (که در فراخوانی بالا ، سمت چپ عملگر : همان متغییر a هست) را بصورت مخفیانه به عنوان پارامتر تابع بفرستیم ، از این عملگر استفاده میکنیم یعنی هر کجا که علامت کلون استفاده شد در پارامتر آن ، آرایه یا متغییری که در کد بالا استفاده میکردیم ، حذف میکنیم .حالا نکته اینجاست که اگر در موقع فراخوانی تابع از علامت کلون استفاده کردیم فقط میتوانیم این پارامتر را از قسمت فراخوانی این تابع حذف کنیم و دیگر نباید این پارامتر را موقع هدر و تعریف تابع حذف کرد . یعنی :



Account = {balance = 0,x=3}


function Account.withdraw(ArrayPar,v)
ArrayPar.balance = ArrayPar.balance - v
return ArrayPar.balance
end


a = Account;
Account = nil
Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);


در کد بالا ، چون فقط در خط آخر یعنی موقع فراخوانی تابع ، از علامت کلون استفاده کردیم پس فقط میتوانیم از همین قسمت این پارامتر را که قبلا به عنوان پارامتر a جایگزین کرده بودیم ، حذف کنیم ولی در قسمت تعریف تابع چئت از این علامت استفاده نکردیم ، پس نباید این پارامتر را از آنجا حذف کنیم . عکس این گفته هم صادق است
نکته ی مهمی که در قضیه ی کلون است ، این است که همونطور که قبلا گفته شد ، این پارامتر فقط بنام self یا this فقط در تابع شناخته میشود البته در صورتی که مخفی باشد یعنی از علامت کلون استفاده شود پس اگر در تعریف تابع بالا یعنی Account.withdraw ، اگر از علامت کلون استفاده کردیم و مجبور شدیم که ArrayPar رو پاک کنیم از پارامترش ، حالا باید در بدنه ی تابع ، بجای متغییر ArrayPar از نام متغییر self (همه ی حروف ها کوچک) استفاده کنیم یعنی :


Account = {balance = 0,x=3}




function Account:withdraw(v)
self.balance = self.balance - v
return self.balance
end




a = Account;
Account = nil
Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);










حالا به مبحث اصلی مان که File I/O هست میپردازیم :

نکته ی بسیار مهمی که همان ابتدای کار باید بیان شود این است که برای استفاده (خواندن یا نوشتن) از یک فایل ، ابتدا باید آن فایل را برای قصد مورد نظر (منظور برای خواندن یا اینکه برای نوشتن است) باید باز شود و نکته ی مهم دیگر این است که وقتی کارمان تمام شد ، باید آن فایل را ببندیم. (منظور از باز کردن و بستن ، دابل کلیک روی فایل نیست . بلکه با توابع file i/o لوا باید این کار را انجام داد که در ادامه عرض میشود)
دقت کنید باز کردن یک فایل به معنای خواندن محتوای آن نیست . باز کردن فایل هم با تابع io.open در لوا انجام میگیرد . توضیحات این توابع بصورت انگلیسی در لینک زیر موجود است که در ادامه به بررسی مهم ترین این توابع میپردازیم :

Lua 5.1 Reference Manual (Only the registered members can see the link-io.close)

در فرآیند باز کردن یک فایل ،باید به این چند نکته هم توجه کرد :
اول و مهمترین آن اینکه میخواهیم محتوای فایل مورد نظر را بخوانیم یا بنویسیم . همانطور که گفته شد ، قبل از هر عملیات خواندن یا نوشتن ، ابتدا باید فایل را باز کرد اما نکته ی حائز اهمیت اینجاست که باز کردن فایل برای خواندن و باز کردن فایل برای نوشتن متفاوت است یعنی اگر قصد خواندن یک فایل را داریم ، قبل از خواندن ، باید فایل مورد نظر را برای خواندن باز کنیم و اگر قصد نوشتن آنرا داریم ، قبل از آن باید فایل را برای نوشتن باز کنیم . دقت کنیم که اگر قصد نوشتن فایل را داشته باشیم و قبل از آن ، فایل را برای خواندن باز کرده باشیم ، عملیات نوشتن روی آن فایل انجام نمیشود و برعکس اش هم صادق است
دوم آنکه موقع باز کردن فایل ، مشخص کنیم که آن فایل را برای حالت باینری میخواهیم باز کنیم (جهت خواندن یا نوشتن) و یا حالت متن معمولی . برای تشخیص اینکه یک فایلی به حالت باینری است یا متن معمولی ، آنرا با notpad باز کنید و اگر محتوای آن فقط و فقط حاوی متن قابل تشخیص بود ، بصورت متن عادی آن فایل ذخیره شده است و اگر غیر از این بود یعنی حاوی کاراکترهای ناشناخته و درهم بود ، (که معمولا غیر از فایل های txt ، بقیه این طور هستند) آن فایل بصورت باینری هست و برای باز کردن آن (جهت خواندن یا نوشتن آن فایل) صورت باینری باز شود. مشخص هست که اگر با توابع باینری لوا مثل تابع string.dump اگر کار میکنیم ، برای ذخیره کردن و نوشتن در داخل فایل ، باید آن فایل را بصورت باینری برای نوشتن باز کنیم
دقت کنید که عملیات باز کردن حتی برای فایلی که از قبل وجود ندارد و تازه میخواهیم آن فایل را بنویسیم و تولید کنیم هم باید انجام شود

در توابع File I/O ، دو دسته توابع وجود دارند که در واقع فرق شان در علامت کلون یا علامت : است . آن توابعی (در واقع ماژول) که بصورت عادی یا علامت دات (نقطه) دارند مثل io.input و ... ، معمولا مسیر فایل مورد نظر را بصورت پارامتر تابع دریافت میکنند مثلا در همین



io.input ([file])


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



file:read()


متغییر file (که قبل از علامت کلون : آمده است) یک متغییریست که بجای رشته ی مسیر فایل (که به عنوان پارامتر ورودی تعریف میشد در توابع بدون کلون) ، هندل که شماره ای خاص از آن فایل است را میگیرد . هندل هم در ادامه بیشتر میپردازیم اما فعلا همین قدر کافی است که تابع io.open علاوه بر باز کردن (منظور خواندن و نوشتن نیست) فایل مورد نظر ، هندل آنرا برمیگرداند که میتوانیم از متغییری که از این تابع برگردانده میشود ، در تابعی با علامت کلون مثل file:read قبل از متغییر کلون که file بود ، بجایش استفاده کنیم



حالا به بررسی توابع File I/O بپردازیم :

همانطور که گفته شد ، توابع file i/o به دو دسته ی عملگر کلون دار و بدون کلون تقسیم میشوند اما توابع با کلون ، به مانند توابع بدون کلون ، کامل نیستند اما چون توابعی که علامت یا بهتر بگوییم عملگر کلون دارند ، فقط هندل فایل را میخواهند و مسیر فایل به عنوان آرگومان از آنها حذف شد ، پس کار کردن با آنها راحت تر است و ما اغلب روی این توابع کلون دار کار میکنیم تا جایی که لازم باشد
نکته اینکه در هر کجا وقتی آرگومانی را داخل علامت کلوشه یا [ ] نمایش دادند ، یعنی آن آرگومان قرار دادن یا ندادنش اختیاریست و اگر قرار ندهیم ، مقداری پیش فرض را خود زبان لوا قرار میدهد

تابع io.open



io.open (filename [, mode])


همانطور که درباره ی این تابع در بالا تقریبا توضیح کامل داده شد (حتما خطوط بالا را مطالعه بفرمایئد) ، این تابع وظیفه ی باز کردن یک فایل جهت خواندن یا نوشتن را بر عهده دارد و قبل از عملیات خواندن و نوشتن ، باید از این تابع استفاده شود . این تابع ، مقدار عددی هندل آن فایل را برمیگرداند که اغلب در توابع کلون دار این مقدار بکارمان میآید
همانطور که مشخص است ، آرگومان filename ، مسیر فایلی که میخواهیم آنرا باز کنیم را باید بدهیم پس آرگومانی رشته ای است
آرگومان دوم mode همانطور که گفته شد چون داخل علامت کلوشه [ ] هست پس اختیاری است و بصورت پیش فرض اگر مقداری ندهیم ، مقدار "r" را میدهد. این آرگومان رشته ایست و نوع باز کردن یک فایل را از ما میخواهد و باید یکی از پارامترهای زیر را به عنوان رشته به آن داد :
رشته "r" : همانطور که قبلا گفته شد ، اگر قصد داریم محتوای یک فایل را بخوانیم ، قبل از آن باید آن فایل را برای خواندن باز کنیم که این رشته در آرگومان دوم همین کار را انجام میدهد اما اگر میخواهیم در فایل چیزی بنویسیم ، باید آن فایل را برای نوشتن باز کنیم و نمیشود فایلی را برای نوشتن باز کنیم و برای خواندن آنرا باز نکنیم اما محتوای آن فایل را بتوانیم بخوانیم (دقت کنیم ، باعث نمیشود که فایل خوانده شود . فقط به منظور خواندن ، آنرا باز میکند)
رشته "w" : فایل را برای نوشتن باز میکند (دقت کنیم ، باعث نمیشود که فایل خوانده شود . فقط به منظور خواندن ، آنرا باز میکند)
رشته "a" : برای نوشتن یا خواندن ، فایل را از ادامه باز میکند و میشود مثلا فایل را از ادامه ی آخرین سطر نوشت اما مثلا "w" فایل را از ابتدا اور رایت میکند و ...
یک رشته ی دیگر هم هر کدام یک علامت + در آخر میگیرند که برخلاف توضیحات سایتش ، مثل حالت عادی باعث اوررایت میشود (چیزی که باعث آپدیت میشود فقط رشته ی "a" هست)
باز هم آخر همه ی این رشته ها را اگر کلمه ی b بنویسیم یعنی اگر بنویسیم "rb" یا "wb" یا "ab" یعنی اینکه بجای فایل متن و txt ، میخواهیم فایل باینری را برای خواندن یا نوشتن یا آپدیت کردن ، باز کنیم (فایل باینری هم هر فایلی را که با notepad باز کنیم و غیر از متن ، علامت های درهم نوشته شده باشد را میگویند)
تابع io.open تنها تابع مهم قسمت file i/o است که برایش علامت همچین تابعی با کلون در نظر گرفته نشده . حالا توابع کلون دار

تابع file:lines



file:lines()


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


io.open (filename , "r")

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



for OneLine in file:lines() do
--body
end


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

تابع file:read



file:read(···)


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


io.open (filename , "r")

اگر فایل مورد نظر بصورت بایتی هست ، باید رشته ی b را به آخر آن اضافه کرد
تابع بالا ، فایل را بر اساس چیزی که ما میگوییم ، میخاند . اینکه بر چه اساسی بخواند ، یک آرگومان به عنوان ورودی به این تابع میدهیم که مشخص کننده ی نوع خواندن آن است . این ورودی ممکن است رشته یا عدد باشد که در ادامه توضیح میدهیم :
رشته ی
"*n" . دقیق کار این آرگومان مشخص نیست اما در توضیحات سایت آمده که عدد را میخواند و عدد را برمیگرداند
رشته ی
"*a" که یکی از مهمترین آرگومان ورودی این تابع است ، باعث میشود این تابع کل فایل را بخواند و کل متن آنرا برگرداند (دقت کنید اگر فایل بزرگ باشد (مثلا فایل ویدئویی بالای 200 مگ و ...) ، این تابع نمیتواند کل متن فایل را در داخل یک متغییر بریزد و در واقع نمیتواند کل فایل را یک دفعه بخواند . در این مواقع باید با تابع iterator ای که در بالا توضیح داده شد file:lines ، خط به خط فایل را جداگانه بخوانیم یا از آرگومان دیگری که در ادامه برای این تابع توضیح داده میشود استفاده کنیم).
رشته ی
["*l"/CODE] طبق توضیحات سایت لوا ، باعث خوانده شدن خط بعدی میشود
در آرگومان دیگر از همین تابع file:read ، بجای رشته های بالا ، اگر عدد وارد کنیم ، این تابع فقط همان تعداد متن را از ابتدای آن فایل میخواند . مثلا اگر بنویسیم [CODE]file:read(20) باعث میشود 20 کاراکتر اول از آن خط خوانده شود . برای اینکه فایل هر تعداد کاراکتر خاص را پی در پی بخواند یعنی مثلا هر 20 کاراکتر 20 کاراکتر را بخواند ، مشخص است که باید از یک حلقه ی تکرار با این تابع استفاده کنیم و یا تابع iterator این کار را کنیم

تابع file:write



file:write(···)


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


io.open (filename , "w")

اگر فایل مورد نظر بصورت بایتی هست ، باید رشته ی b را به آخر آن اضافه کرد
هر آرگومانی از نوع عدد یا رشته به تابع بالا بدهیم ، آنرا در فایل مورد نظر مینویسد . برای تبدیل انواع داده ای جز عدد و رشته ، از توابع tostring و string.format استفاده کنید قبل از نوشتن
این تابع میتواند هر تعداد آرگومان داشته باشد که به ترتیب این تابع ، آرگومان ها را پشت سر هم مینویسد

تابع file:close



file:close()


این تابع هیچ آرگومانی را به عنوان ورودی نمیگیرد و مقداری را هم برنمیگرداند . این تابع باعث بستن فایلی که باز کردیم میشود . اگر فایل مورد نظر را با این تابع نبندیم ، موقع حذف آن ، سیستم پیام file in use را میدهد و تا زمانی که نرم افزار مورد نظر (AMS و ...) را نبندیم و یا گاها سیستم را ریستارت نکنیم ، فایل را نمیتوانیم حذف کنیم



مثال ها :

1) میخواهیم فایلی را بنام Document.txt در پوشه اصلی (CD_Root) بخوانیم (در فایل Document.txt متن دلخواه را وارد کنید و ذخیره کنید) :



FileHandle = io.open("Document.txt", "r");
FileContent=FileHandle:read("*a");
Dialog.Message("Notice", FileContent, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
FileHandle:close();


خوب اول باید فایل مورد نظر باز شود پس از تابع io.open استفاده میکنیم و در آرگومان اول آن ، مسیر فایل را میدهیم. در آرگومان دوم چون میخواهیم فایل را بخوانیم ، پس برای خواندن باید آنرا باز کنیم و چون فایل txt هست یعنی متن در هم نیست پس فایل باینری نیست پس در آرگومان دوم آن ، فقط رشته ی "r" را وارد میکنیم (دقت کنید حروف کوچک و بزرگ را) . فایل Document.txt را برای خواندن باز میکند و عددی را به عنوان هندل این فایل ، برمیگرداند پس در متغییر این تابع که FileHandle هست ، هندل این فایل ذخیره میشود
همونطور که گفته شد ، در توابع های کلون دار که در بالا ذکر شد ، بجای file یعنی قبل از علامت کلون گفتیم که باید از هندل فایلی که از خروجی تابع io.open استفاده میشود (در مثال بالا همان متغییر FileHandle ) استفاده کنیم. یعنی قبل از توابع کلون دار ، در مثال بالا ، باید بنویسیم FileHandle که تابع باید بداند که به کدام فایل این عملیات را انجام دهد (همانطور که گفته شد ، در توابع کلون دار ، مسیر فایل از آرگومان تابع حذف شد و به هندل فایل قبل از علامت کلون انتقال داده شد)
پس برای خواندن فایل باید از تابع FileHandle:read استفاده کنیم . آرگومان رشته ی "*a" در این تابع یعنی اینکه کل محتوای فایل را بخوان و در متغییر خروجی این تابع که FileContent است بریز
خط بعد هم که محتوا را چاپ میکند با پیغام
خط آخر هم که مثل ساختار فایل های کلون که در خط بالا گفته شد باید قبل از علامت کلون ، از هندل فایل استفاده کرد ، پس FileHandle:close() باعث بستن فایلی که هندل FileHandle داشت میشود . دقت کنید قبل از استفاده از از این تابع (مثلا موقعی که فایل خوانده شد و پیام داد در خط سوم) اگر فایل مورد نظر را بخواهیم حذف کنیم ، با پیام file in use ویندوز مواجه میشویم اما بعد از خط آخر میتوانیم حذف آنرا


2) متنی را داخل فایل Document.txt در پوشه اصلی بنویسیم :



FileHandle = io.open("C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\Document.txt", "w");
FileContent=FileHandle:write("this is a write test into taste file");
FileHandle:close();


اگر دیدید فقط با آوردن رشته ی "Document.txt" عمل نمیکند ، رشته ی کامل مسیر را بصورت دستی بیاورید (بجای UserName ، نام کاربری خود را در رشته ی اول بدهید) . حتی سعی کنید از متغییر _SourceFolder هم استفاده نکنید شاید جواب ندهد
چون قصد نوشتن داشتیم پس باید برای نوشتن باز کنیم
دقت کنید در مثال بالا (موقع نوشتن) ، حتی اگر فایل مورد نظر هم وجود نداشته باشد ، ابتدا باید آنرا باز کرد برای نوشتن و بسیار مهم است این نکته

3) در این مثال ، فایل Document.txt در پوشه ی اصلی را خوانده و در پیامی چاپ و بعد از آن متن هایی به آن میخواهیم اضافه کنیم :



Path="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\Document.txt";
ReadHandel = io.open(Path, "r");
FileContent=ReadHandel:read("*a");
Dialog.Message("Notice", FileContent, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
WriteHandel = io.open(Path, "a");
WriteHandel:write("\n","this line is one line remain to last line","\n","this line is last line . End")
ReadHandel:close();
WriteHandel:close();


بجای UserName ، نام کاربری خود را در رشته ی اول بدهید
اولا چون هم میخواهیم بنویسیم و هم بخوانیم پس باید 2 بار فایل را باز کنیم . یکبار برای خواندن که "r" را در آرگومان مینویسیم و در متغییر ReadHandel ذخیره کردیم پس هر تابعی مربوط به خواندن مثل خط سوم را همراه با این هندل و متغییر میآوریم و یکبار دیگر برای نوشتن هم باید فایل را باز کرد (در خط پنجم) و در متغییر WriteHandel ، هندل این فایل برای نوشتن را ذخیره کردیم . منتها از "w" به عنوان آرگومان این تابع استفاده نکردیم چون میخواستیم ، در ادامه ی آن متن اضافه کنیم و اوررایت نکنیم . اگر از "w" استفاده میکردیم ، متنهای قبلی پاک میشد پس برای آپدیت و اضافه کردن متن ، از رشته "a" بجای "w" در آرگومان این تابع استفاده کردیم
دوما در خط 3 تا مانده یه آخر هم ، باید از هندلی که برای آپدیت کردن در خط 5 داخل متغییر WriteHandel ریختیم ، استفاده کنیم . این تابع ، دانه دانه آرگومان ها را شروع به اضافه کردن در فایل مورد نظر میکند یعنی اول ، آرگومان اول را که باعث اینتر زدن یا رفتن به خط بعد در فایل میشود را مینویسد و بعد آرگومان دوم که یک متن است و سپس آرگوامن بعدی که باز هم به خط بعد میرود و سپس آرگومان آخر را که متنی دیگر است
در دو خط آخر هم که چون 2 بار فایل را باز کردیم ، یعنی یکبار برای خواندن و یکبار هم برای نوشتن ، پس حتما حتما باید 2 بار آنهم هر بار با هندل های مورد نظر (یکبار با هندل خواندن و یکبار هم با هندل نوشتن) فایل را ببندیم پس به این ترتیب ، ReadHandel:close() و WriteHandel:close() را در 2 خط آخر مینویسیم (این 2 خط آخر یکی از نکات بسیار مهم است که نباید فراموش کرد)

4) یک فایل pdf بنام OldPdf در پوشه سورس ایجاد کنید . با کد زیر ، اطلاعات فایل را خوانده و در فایل txt با نام NewPdf میریزد :



PathSource="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\OldPdf.pdf";
PathExit="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\NewPdf.txt";
FileRead=io.open(PathSource ,"rb");
FileWrite=io.open(PathExit ,"wb");
PdfContent = FileRead:read("*a");
FileWrite:write(PdfContent);
FileRead:close();
FileWrite:close();


بجای UserName ، نام کاربری خود را در رشته ی اول بدهید
هر فایل pdf را که با notepad باز کنید ، میبینید که کلمات درهم برهم نوشته شده است پس فایل باینری ست و برای باز کردن (چه برای خواندن یا نوشتن) باید بصورت باینری باز شود پس موقع باز کردن ، در آخر دومین آرگومان ، کاراکتر b را اضافه میکنیم (این بسیار مهم است) یعنی در خط 3 و 4
خط 5 هم کل محتوای فایل pdf را میخواند و در خط بعد هم کل این محتوا را در داخل فایل NewPdf.txt مینویسد (میتوانیم حتی برای این فایل ، هر پسوند دیگر یا حتی بدون پسوند ذخیره کنیم و در نظر بگیریم)
حالا فایل تکست NewPdf را اگر با نرم افزاری که فایل سورس اصلی را که acrobat reader بود اجرا کنید ، میبینید اجرا میشود و نحوه ی کپی کردن اطلاعات یک فایل میتواند این روش باشد . اما دقت کنید که اگر فایل حجیم باشد (مثلا فایل ویدئویی بالای 100 مگ) بسته به میزان رم ، اطلاعات نمیتواند داخل یک متغییر ذخیره شود . در این صورت باید خط به خط اطلاعات باینری فایل مورد نظر خوانده شود و داخل یک آرایه ذخیره شود (اگر از این روش ، یعنی از تابع iterator استفاده و اطلاعات را داخل آرایه بریزیم ، دیگر نمیشود اطلاعات فایل مبداء را مثل کد بالا ذخیره کنیم (ذخیره میشود) اما دیگر کدهای ذخیره شده در فایل قابل استفاده نیست و فایل اجرا نمیشود. کلا برای اجرای فایل ، ظاهرا یک روش وجود دارد و آنهم خواندن یکباره ی کل فایل و ذخیره در فایل جدید است)

5) کد زیر ، محتوای فایل OldFile را خط به خط میخواند :



PathSource="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\OldPdf.pdf";
NewFile={}
Counter=1;
FileRead=io.open(PathSource ,"rb");
for Line in FileRead:lines() do
NewFile[Counter]=Line;
Dialog.Message("FileLine", NewFile[Counter], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
Counter=Counter+1;
end
FileRead:close();


در خط 5 ، تابع تکرار کننده (iterator) ی FileRead:lines را اجرا و هر بار ، هر خط را در متغییر Line ذخیره میکند
همانطور که گفته شد ، اگر با این روش ، اطلاعات خوانده شده را در فایلی ذخیره کنیم ، آن فایل قابلیت اجرا ندارد و تنها روش برای این کار ، خواندن یکباره ی کل محتویات فایل است






حالا به قضیه ی توابع file i/o هایی که بدون علامت کلون هستند میپردازیم :

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

تابع io.input



io.input ([file])


بعد از تابع io.open و باز کردن فایل ، تابع io.input باعث میشود اگر در تابع ای که مربوط به عمل خواندن میشود (از این به بعد هر جایی تابع گفتیم ، منظور تابع file i/o بدون عملگر کلون است) ، مسیر فایل را ذکر نکنیم ، اتوماتیک هندل و مسیر این فایل که داخل آرگومان file که از هندل خروجی تابع io.open داده بودیم را ست میکند در غیر این صورت ، باید در همه ی آرگومان های بقیه ی توابع ، هر کجا آرگومانی بنام file را دیدیم ، مسیر کامل فایل را به آن بدهیم

تابع io.read



io.read (···)


که باید بصورت زیر نوشته شود :



io.input():read


و دقیق کار تابع کلون دارfile:read را میکند. یا باید در خط بالا ، قبل از علامت کلون ، از خروجی متغییر io.input که قبلا تعریف کرده بودیم ، استفاده کنیم

تابع io.output



io.output ([file])


در آرگومان آن ، بجای file از خروجی تابع io.open استفاده میکنیم و دقیق برعکس تابع io.input ، موقع کار با تابع نوشتن یعنی تابع io.write مورد استفاده قرار میگیرد

تابع io.write



io.write (···)


هر چه در آرگومان آن نوشته شود را در فایل مینویسد . اما باید به این صورت فراخوانی شود :



io.output():write


یا قبل از عملگر کلون ،باید از متغییر استفاده شده در تابع io.output استفاده کنیم

تابع io.close



io.close ([file])


باعث بشته شدن فایل مورد نظر میشود

تابع io.lines



io.lines ([filename])


همان کار تابع file:lines را انجام میدهد.
بجای filename باید اسم فایل را نوشت

تابع io.type



io.type(obj)


یکی از توابع کاربردی که در قسمت توابع کلون دار وجود ندارد ، این تابع هست که اگر فایلی باز شده بود ، مقدار رشته ی "file" را برمیگرداند و اگر بسته بود مقدار رشته ی "closed file" را برمیگرداند و اگر ارور بدهد ، nil را
در آرگومان این تابع هم بجای obj ، مقدار خروجی تابع io.open را که همان هندل فایل مورد نظر بود را وارد کنید



مثال بدون توضیح :
1)



-- Opens a file in read
file = io.open("test.lua", "r")

-- sets the default input file as test.lua
io.input(file)

-- prints the first line of the file
print(io.read())

-- closes the open file
io.close(file)

-- Opens a file in append mode
file = io.open("test.lua", "a")

-- sets the default output file as test.lua
io.output(file)

-- appends a word test to the last line of the file
io.write("-- End of the test.lua file")

-- closes the open file
io.close(file)




2)



-- Opens a file in read mode
file = io.open("test.lua", "r")

-- prints the first line of the file
print(file:read())

-- closes the opened file
file:close()

-- Opens a file in append mode
file = io.open("test.lua", "a")

-- appends a word test to the last line of the file
file:write("--test")

-- closes the open file
file:close()

SajjadKhati
15-03-16, 12:21
سلام
آقا من فراموش کردم این عکس زیر رو به اولِ پست 217 (دو پست بالاتر) الحاق کنم
یکی زحمت شو قبول میکنه؟
ممنون

Only the registered members can see the link

همینطور عکس های زیر توی جای دیگه آپلود شده بود که اگه یه جا باشه ، امکان از دست رفتنش شاید باشه . اینجا هم دوباره میزارم :

Only the registered members can see the link


Only the registered members can see the link


Only the registered members can see the link


Only the registered members can see the link

SajjadKhati
07-05-16, 20:42
نکته درباره کمپوننت اتوات :

اولا اتوات ، نسخه ی 64 بیت اش رو نباید استفاده کرد یعنی فایل AutoItX3_x64.dll رو نباید استفاده کنیم
بعد اینکه کمپوننت اتوات رو در ویندوز 64 بیتی ، باید در پوشه ی SysWOW64 و در ویندوز 32 بیتی در پوشه ی System32 کپی و بعد ثبت کرد (با تابع System.RegisterActiveX هم میشه یک کمپوننت ای که میخوایم اضافه کنیم مثل همین اتوات رو ثبت کرد منتها باید قبل اش طبق چیزی که گفته شده ، در پوشه ی مناسبی طبق نسخه ی ویندوز کپی کرد)

SajjadKhati
26-05-16, 16:48
نکته درباره پترن ها (یادآوری) :


علامت ها تعیین کننده ی الویت هستند .
علامت مثبت + : یعنی حداقل یکی از اون الگو (پترن) باید پیدا بشه تا جوابی پیدا بشه و اگه هم اون پترن پشت سر هم بود ، تا آخرِ اون پترن رو برمیگردونه (بزرگترین نمونه) مثلا "+d%" کل اعداد رو برمیگردونه
علامت منفی - : یعنی بودن یا نبودن اون پترن مهم نیست (اگه بود ، توی نتیجه میاد و اگه نبود توی نتیجه نمیاد ولی بالاخره نتیجه پیدا میشه) یعنی به عبارت ریاضی اگه بخواد گفته شه ، حداقل صفر تا از اون الگو باید موجود باشه (به زبان عامیانه یعنی موجود بودن یا نبودن اش فرقی نداره) و اگه اون پترن پشت سر هم بود ، کوچیکترین نمونه ی اون پترن برگردونده میشه (توضیح این تیکه در آموزش ها اومد)
علامت ضرب * : مثل منفی ، بودن یا نبودن اون پترن براش مهم نیست اما مثل ضرب ، تا آخرِ اون پترن (بزرگترین پترن) رو برمیگردونه
علامت علامت سئوال ? (علامت سئوال بصورت انگلیسی) : مثل منفی ، بودن یا نبودن پترن براش مهم نیست ، اما فقط اولین کاراکتر از کل اون پترن رو برمیگردونه یعنی مثلا اگه در "586013 "پترن "?d%" رو بنویسیم ، ضمن توضیح الویت که گفتم ، فقط اولین کاراکتر رو که "5" هست را برمیگردونه




البته استثناهایی داره که توی آموزش بررسی شد
دقت کنیم ، همونطور که در آموزش بررسی شد اگه Magic کاراکترهای - و * و ? اگه به تنهایی فقط برای یک پترن (کلاس کاراکتر) بکار بره ، چون الویت اول رو ندارن ، ارور میده یعنی نمیتونیم به تنهایی بنویسیم "%d-" یا "%d*" یا "%d?" یا "[%d.abc%p]-" یا همه ی پترن ها و کلاس کاراکترهامون رو یکی از این 3 الویت بگیریم (مثلا 2 تا کلاس کاراکتر که هر 2 تا منفی باشن ، نمیشه ولی یکی منفی و یکی ضرب احتمالا میشه) . کلا باید پترن ها (کلاس کاراکترها) حداقل شامل یک دونه علامت با الویت بالاتر از بقیه ی پترنهای (کلاس کاراکترها) کناریش باشه (یا باید بدون علامت باشه که الویت اش برابر علامت مثبت + هست)
علامت کلاس کاراکتر کلوشه [] یعنی هر پترنی توش قرار بگیره ، ترتیب اش مهم نیست
پس علامت های +-*? الویت ها رو عوض میکنن و علامت کلوشه ، ترتیب ها رو

SajjadKhati
05-06-16, 17:37
Garbag Collector در لوا و آزاد کردن رم :

برای آزاد کردن حافظه بعد از حذف آرایه ای که از ریشه اون آرایه رو nil کردین (nil کردن اعضای اون آرایه مهم نیست و تاثیری در کار نداره) فقط کافیه تابع



collectgarbage();


را بدون هیچ آرگومان ورودی ای اجرا کنید (بعد از nil کردن ریشه ی آرایه یا متغییر) تا گربگ کالکتور لوا ، حافظه ای که برای اون آرایه در نظر گرفته بود رو آزاد کنه

مثال درست (این مثال درست هست چون آرایه از ریشه nil شد) . در یک رویداد این کد رو بنویسید تا رمتون تا 500 مگ اشغال بشه:



arr={};
for i=1,20000000 do
arr[i] = "abcdefghijklmnopqrstuvwxyz"
end


و در رویدادی که میخواین آزاد بشه رم تون از این آرایه ، این کد رو بنویسین :



arr=nil;
collectgarbage();



مثال نادرست (این مثال نادرست هست چون ریشه ی آرایه رو nil نکردیم و فقط کل اعضای اون آرایه رو nil کردیم که جواب نمیده) . در یک رویداد همون کد اولی :



arr={};
for i=1,20000000 do
arr[i] = "abcdefghijklmnopqrstuvwxyz"
end


در یک رویداد دیگه (که البته رمتون آزاد نمیشه و کد کار نمیکنه) :



for i=1,20000000 do
arr[i] = nil;
end

collectgarbage();

SajjadKhati
06-06-16, 22:10
سلام

یاهو (یاهو و گوگل نه . آوای خوشحالی منظورمه:lol:) . آخه این الگوریتم به این سادگی چی بود که من نتونستم اون موقع که file i\o رو بررسی میکردم متوجه شم:Love-ssa~! (1):. اینجاست که امام خامنه ای میگه به وعده ی خدا اعتماد داشته باشید (إِن تَنصُرُوا اللَّهَ يَنصُركُم) . حتما اون موقع اعتمادم کمتر بود :1. (23):

این کد رو برای تیکه کردن یک فایل بدین :



psdPath=_SourceFolder.."\\Document.pdf";
pdfSaveName = _SourceFolder.."\\New folder\\SaveCodeLine";
--film.mpg New folder
pdfOpen = io.open(psdPath , "rb");
Counter = 1;
while (value==nil) do
psdRead = pdfOpen:read(20000);
if (psdRead~=nil) then
saveHandle = io.open(pdfSaveName..Counter..".txt", "wb");
saveHandle:write(psdRead);
saveHandle:close();
Counter = Counter + 1;
else
break;
end
end


pdfOpen:close();




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



openTxt = _SourceFolder.."\\New folder\\SaveCodeLine";
savePdf = _SourceFolder.."\\MyPdf.pdf";


for i=1,5 do
openHandle = io.open(openTxt..i..".txt", "rb");
textContain = openHandle:read("*a");


pdfHandel = io.open(savePdf, "ab");
pdfHandel:write(textContain);
pdfHandel:close();
--openHandle:write("salam 1");


openHandle:close();
end

پارامترهای توابع و همینطور مسیر فایل را و حتی الگوریتم رو طبق خواسته تون تغییر بدین

این کد میتونه هر فایلی در هر حجمی را تیکه کنه

فایل های تیکه شده رو (اگه فیلم و ویدئویی و از این دست باشه باشه نه exe و اینا) اگه با فرمت خودش ذخیره کنیم ، میشه جداگانه هم اجرا کرد.

حالا میدونین دیگه . میتونین روی هر تیکه از فایل ، انکد خاص خودتون رو داشته باشین با توابع Crypto (البته هدف از تیکه کردن فایل ، انکد کردن شون نیست)
راستی اینم بگم که البته توی کد دوم (وصل کردن فایل ها) ، کدی که من گذاشتم ، خوب تعداد فایل هایی که تیکه کردم رو میدونستم و 5 تا بود . واسه همین توی حلقه ی 5 تایی گذاشتم . شما باید به تعداد تیکه های فایل تون این حلقه را بزارین
چقد دنبال تیکه کردن فایل بودم :lol: الگوریتم به این چندان سادگی را نمیدونم چرا قبلا یادم نیومد:1. (23):

SajjadKhati
07-06-16, 01:53
نکته درباره File i\o :

اول اینکه وقتی کلا خط به خط میخونیم و ذخیره میکنیم اطلاعات ذخیره شده خراب از آب درمیاد ! حالا چه با تابع file:lines() یا با تابع حتی file:read("*l"); فقط باید یا کل فایل را یکجا بخونیم و توی متغییر بریزیم و یا اگه فایل حجیم هست (بالای 200 مگ) و داخل متغییر جا نمیشه ، باید تعداد کاراکتری که میخونیم رو با استفاده از تابع file:read و نوشتن تعداد کاراکترهای مورد نیاز در آرگومان این تابع (نوشتن شماره) و قرار دادن در حلقه while ، به همون تعداد از کاراکترهای اون فایل را بخونیم و توی متغییر ذخیره و توی فایل جداگانه ذخیره کنیم (چرا خوندن خط به خط جواب نمیده ، نمیدونم)

دوم اینکه میشه اطلاعات فایل های باینری رو داخل آرایه ریخت اما وقتی میخوایم آرایه ها را به هم متصل کنیم چون یا بصورت دستی مجبوریم یه رشته ی خالی را برای مقدار ذخیره ی خونه های قبلی آرایه برای جسبوندن استفاده کنیم ، و وقتی هم که به رشته ی حاوی اطلاعات باینری ، کوچیک ترین چیز و یا رشته و یا کاراکتری اضافه کنیم (حتی اگه رشته ی خالی که "" هست رو اضافه کنیم) ساختار باینری و اطلاعاتش کاملا به هم میریزه بنابراین نمیشه اطلاعاتی که داخل آرایه هست رو توی خروجی فایلی ذخیره کنیم (ذخیره میشه ولی اطلاعات آرایه ی ذخیره شده ، اشتباه هست و فایل نهایی ارور میده) و یا اینکه برای اتصال اعضای آرایه از تابع Table.Concat (برای اتوپلی) و یا تابع table.concat (برای لوا) استفاده میکنیمم که هردوی این توابع در آرگومان دوم شون حداقل رشته ی خالی میگیرن برای جدا کردن اعضای آرایه و چون گفته شد که رشته ی حتی اگه هم خالی باشه باعث به هم ریختن اطلاعات رشته ی باینری میشه ، این روش هم نمیشه بنابراین تا جایی که میدونم ، اطلاعات داخل آرایه رو نمیشه به عنوان اطلاعات درست باینری ذخیره کرد.
البته اطلاعات باینری ذخیره شده در آرایه (اگه با تابع file:read() باشه برای اطلاعات باینری (برای اطمینان از این تاب استفاده کنین)) ، درست هست ولی فقط ذخیره نمیتونیم کنیم اطلاعات آرایه رو داخل فایل

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

SajjadKhati
03-07-16, 08:56
یوهو پیدا کردم بعد از یه قرن
خدایا شکرت
الان در پوست خودم گنجایش ندارم :Love-ssa~! (1)::Love-ssa~! (1)::Love-ssa~! (1)::Love-ssa~! (1):
باز هم معنای إِن تَنصُرُوا اللَّهَ يَنصُركُم که امام خامنه ای گفت برام مجسم شد
استاد علی خیلی از راهنمایی تون ممنونم . واقعا لطف بزرگی کردین . دیگه هم لازم نیست وقتم رو روی زبان های دیگه برای ساخت dll برای زبان لوا صرف کنم

تجربه ی خودمو بگم تا فراموش نکردم


آموزش ساخت dll در #C برای زبان های غیر دات نت (Unmanaged Export) :

1) اول نوع پروژه رو Class Library انتخاب کنین (پروژه ی Class Library Portable انگار بخاطر اینکه نمیتونه Unmanaged Exports رو نصب کنه (البته برای من) پیشنهاد نمیشه)

2) بعد از منوی Project ، گزینه ی آخر رو انتخاب کنین (گزینه ی آخر ، نام پروژه و در ادامه اش کلمه ی properties داره) و در سربرگ دوم که Build هست برین و گزینه ی platform target (در وسط این سربرگ) و گزینه ی target (در بالای این سربرگ) رو روی X86 بزارین (چون اتوپلی ، نرم افزار 32 بیتی هست) و کلا به هیچ وجه نباید موقع Unmanaged Export ، روی حالتی غیر از x86 یا x86 که حالا بسته به نوع نرم افزار مقصدتون که چند بیتی رو ساپورت میکنه باشه (یعنی به هیچ وجه نباید روی any cpu تنظیم شده باشن که بصورت پیش فرض هستن و باید تغییر داد همونطور که گفته شد). البته من برای اطمینان ، علاوه بر اینها ، گزینه ای کنار گزینه ی start (که برای اجرا و کمپایل نرم افزار این دکمه ی استارت رو میزنیم) وجود داره که بصورت combo box هست که من از گزینه ی آخر اون combo box ، گزینه ی configuration رو میزنم و از اونجا هم گزینه ی any cpu رو به x86 تغییر میدم


3) بعد تابع تونو بنویسین ( دقت کنین تابع باید از نوع public static باشه تا بدون ایجاد شی و از هر جا بتونه فراخونی بشه) . دقت کنین احتمالا بیشتر از یک تابع و همچین توابع overloade هم نمیشه برای یه کلاس نوشت در حالت Unmanaged Export (توی یه منبع انگلیسی نوشته بود که بصورت بازگشتی هم نمیشه این نوع تابع ای که مینویسیم رو داخل سی شارپ فراخونی کرد و نوشت ولی اینا رو تست نکردم)

4) در منوی Tools ، گزینه ی NuGet Package Manager و بعد زیر منوی Package Manager Console رو انتخاب کنین و کد زیر رو در پنجره ی باز شده (پنجره ی Package Manager Console بنویسین) . البته قبل از نوشتن کد زیر ، مطمئن بشین که ویژال استودیو به اینترنت دسترسی داره :



Install-Package UnmanagedExports


حالا فایل های مورد نیاز برای Unmanaged Exports رو دانلود میکنه (آخرش باید پیام Successfully بده)

5) بعد از دانلود موفقیت آمیز ، فضای نام زیر رو به پروژه تون اضافه کنین :



using RGiesecke.DllExport;


بعد در بالای تابعی که نوشتین ، یه کلوشه باز کنین و در اونجا باید با تابع DllExport ، نام export و همچنین نوع export تون رو بنویسین . نام export تون باید هم نام تابع تون باشه و بصورت یه رشته ، در اولین آرگومان تابع DllExport (که در کلوشه ی بالای نام تابع مینویسین) ، بنویسین . در آرگومان دوم تابع DllExport ، نوع export که اغلب پیشنهاد میشه stdcall رو انتخاب کنین ، بنویسین . به این ترتیب که پروپرتی ای بنام CallingConvention (دقت کنین که در این پروپرتی ، حروف C ، بصورت حروف بزرگ هستند و باید این طوری بنویسین . یک حالت دیگه ی callingConvention بصورت کمل کیس هست که بعدش براتون علامت دو نقل قول میاره که این حالت منظور نیست) رو بنویسین و بعدش علامت مساوی بزارین (=) (البته خود اینتل لایسنس ویژال استودیو براتون میاره) و بعدش در فضای نام System.Runtime.InteropServices (این فضای نام رو بنویسین) و نقطه بزارین و enum ای بنام CallingConvention رو بنویسین (تا اینجا رو بازم خود اینتل لایسنس ویژال استودیو براتون میاره) و بعدش نقطه و بعدش متغییر که همون نوع خروجی مورد نظرتون هست را انتخاب کنین که اغلب نوع stdcall رو انتخاب میکنن برای unmanaged
یعنی به این صورت بنویسین :



[DllExport("MyFunc", CallingConvention = System.Runtime.InteropServices.CallingConvention.S tdCall)]



یه نمونه تابع کوچیک که دو تا عدد رو میگیره و جمع شونو برمیگردونه در سی شارپ که بصورت Unmanaged Export خروجی گرفته شد (البته اول ، نکاتی که گفته شد یعنی از نکته ی 1 تا 4 باید انجام بشه) :



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RGiesecke.DllExport;


namespace ClassLibrary2
{
public class Class1
{
[DllExport("MyFunc", CallingConvention = System.Runtime.InteropServices.CallingConvention.S tdCall)]
public static int MyFunc(int a, int b)
{
return a + b;
}
}
}



در مثال بالا ، به فضای نام RGiesecke.DllExport که اضافه شد و تابع DllExport که در بالای تابعی که نوشتیم (در بالای تابع MyFunc که نوشتیم) که در علامت کلوشه [] هست و آرگومان اول این تابع که نام Export مون که رشته ای هم نام تابع مون هست و همینطور آرگومان دوم این تابع رو دقت کنین)

حالا در اتوپلی برای فراخونی این تابع ، این کد رو بدین (البته در آرگومان اول تابع زیر در اتوپلی ، مسیر فایل dll تون که هر جا میزارید رو بدین) :



result = DLL.CallFunction("AutoPlay\\Scripts\\ClassLibrary2.dll", "MyFunc", "5,10", DLL_RETURN_TYPE_INTEGER, DLL_CALL_STDCALL);
Dialog.Message("Notice", result, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);



راستی اینم بگم که Unmanaged Export هیچ ربطی به نسخه ی دات نت نداره و نسخه ی دات نت میتونه 4.5 و حتی بالاتر هم باشه
بعد دقت کنین چون پروژه رو بصورت x86 تولید کردین ، وقتی dll تون رو build کردین ، دیگه dll تون توی پوشه ی Debug ذخیره نمیشه . بلکه کنار پوشه ی Debug ، پوشه ای بنام x86 ساخته میشه که باز داخل خود همین پوشه ی x86 ، پوشه ی Debug ساخته میشه که dll تون داخل این پوشه قرار میگیره

6) تذکر : اتوپلی با تابع Dll.CallbackFunction اش فقط مقدار عدد و رشته رو میتونه به تابع داخل فایل dll بفرسته و دریافت کنه.
هر چیز دیگه ای بجز این در تابع مون (در dll) برگردونده شه مثل کل آرایه برگردونده شه (عضو خاصی از آرایه که عدد یا رشته رو برگردونه منظورم نیست) ، برنامه اتوپلی (اتوران) ، کرش میکنه
سعی کنید بولین هم ارسال نکنید ولی اشکالی نداره خروجی تابع تون در dll ، بولین برگردونه
مقدار بازگشتی تابع مون (در dll) اگه void بود ، در چهارمین آرگومان تابع DLL.CallFunction مون باید مقدار DLL_RETURN_TYPE_LONG رو انتخاب کنیم وگرنه نوع چیزی رو که برمیگردونه رو باید در چهارمین آرگومان این تابع انتخاب کنیم


باز هم یه تشکر گسترده از استاد علی کنم . خیلی ممنون استاد علی

SajjadKhati
23-10-16, 10:59
آموزش نصب اتوماتیک فایل های msi

یه چند تا پست آموزشی و پروژه هست که تو فیلم ها گفته نشد ، واسه همین گفتم بگم دیگه Only the registered members can see the link

این پست واسه قرار دادن و آموزش نصب اتوماتیک فایل ها (بدون اومدن پنجره ی واسه زدن دکمه های Next و ...) از طریق فایل های MSI :

چیزی نیس که ، یه خطه همش . خوندن نداره که Only the registered members can see the link




InstallMsi = MSI.InstallProduct("AutoPlay\\Docs\\Gmail Notifier Pro 4.5.1.msi", "ACTION=INSTALL");


خوب دیگه ملومه از اسمش دیگه . باید نوع و پسوند فایل تون MSI (نه exe ها) باشه که تک و توک فایل های نصبی مثل نرم افزارهای Google Drive و ... با این پسوند ان (حیف .شانسو میبینی؟ اگه هموشون با این پسوند میشدن ، چی میشد Only the registered members can see the link)
دومین نکته این که تو ورودی اول کد بالا ینی "AutoPlay\\Docs\\Gmail Notifier Pro 4.5.1.msi" باید مسیر فایل MSI ای که میخاین نصب کنین رو بدین (هر پوشه ای که میرین باید دو تا علامت \\ بزارین (یکی نمیشه ها) باز نگین نگفتی Only the registered members can see the link)
سومین نکته هم اینه که همونطور تو راهنمای این دستور ملومه ، اگه میخاین فایل رو حذف کنین (ینی قبلا اگه نصب شده بود) ، باید تو ورودی دوم این کد ، رشته ی "REMOVE=ALL" رو وارد کنین (مثل همینی که نوشتم باید داخل دابل کوتیشن باشه ها ینی نوشته هه باید داخل علامت " " باشه چون از نوع رشته هه) که اینش به درد ما زیاد نمیخوره (حذف کی میکنه آخه Only the registered members can see the link) و اگه میخاین فایل msi رو نصب کنین تو ورودی دوم ، مثل کد بالا باید بنویسین "ACTION=INSTALL" و اگه میخاین بصورت administrator نصب کنین باید "ACTION=ADMIN" رو وارد کنین

اگه نصب با موفقیت انجام شه ، متغییر اش (که تو اینجا طرف چپ مساوی ینی InstallMsi میشه) برابر true میشه ینی true رو برمیگردونه و اگه نصب نشه یا مشکلی پیش بیاد ، false رو برمیگردونه یا همون ذخیره میکنه که تو راهنماش نوشته هه. مثلا اگه میخاین وقتی با موفقیت نصب شد پیام بده ، چون اگه با موفقیت نصب بشه ، متغییرش (InstallMsi) برابر با true میشه پس میگین اگه برابر با true شد ، یه پیام بده ینی اگه میخاین پیام بده ، در ادامه ی کد بالا ، اینو بنویسین :




if InstallMsi==true then
Dialog.Message("Notice", "your program installed successfully", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end


موفق و پیروز و سربلند باشین

SajjadKhati
25-05-18, 00:09
سلام


این کد اتصال usb و کلا device هست و نیازمند فعال سازی پلاگین MemoryEx هست .


در Global Function :



WM_MOVING = 0x0216;
WM_DEVICECHANGE = 0x0219;
DBT_DEVICEARRIVAL = 0x8000;
DBT_DEVTYP_VOLUME = 0x00000002;






function EventHandler(hWnd, uMsg, wParam, lParam)
if (hWnd == mainWinHdl and uMsg == WM_DEVICECHANGE) then
--MainWindows_DEVICECHANGE
if (wParam == DBT_DEVICEARRIVAL) then
-- etesal device
DEV_BROADCAST_HDRStructure = MemoryEx.DefineStruct(
{UINT("dbch_size");
UINT("dbch_devicetype");
UINT("dbch_reserved");
}
);

hdlDEVStruct = MemoryEx.AssignStruct(lParam, DEV_BROADCAST_HDRStructure);
if (hdlDEVStruct.dbch_devicetype == DBT_DEVTYP_VOLUME) then
-- etesal usb
Dialog.Message("Notice", "usb device connected", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
end

end

end

return Subclass.OldWinProc(hWnd, uMsg, wParam, lParam);
end



در OnShow :




mainWinHdl = Application.GetWndHandle();
Subclass.Create(mainWinHdl, EventHandler);


در OnShutDown :



Subclass.Remove(mainWinHdl);