فصل ۳: ماژولها، توابع و Subroutineها
ماژولها ظرفهایی هستند که کد خود را در آنها مینویسید. Function و Subroutine دو روش متفاوت برای ساختن یک قطعه کد اجراییاند.
ماژولها
ماژولها صفحههای کدی هستند که به برنامهٔ شما اختصاص دارند. آنها مستقیماً با Eventهای پایگاهداده اجرا نمیشوند و باید بهطور صریح فراخوانی شوند. ماژول راهی برای ایجاد Procedureهای عمومی است، نه کدی که الزاماً داخل یک شیء خاص مانند Form یا Report اجرا شود. میتوانید آنها را به چند روش فراخوانی کنید:
- از یک فرمان سفارشی Ribbon یا Toolbar استفاده کنید. دربارهٔ سفارشیسازی Ribbon در فصل ۱۱ بیشتر توضیح داده میشود.
- یک کنترل VBA را از Control toolbox مستقیماً در Form یا Report قرار دهید و کد خود را به آن متصل کنید؛ مثلاً کدی برای واکنش به عمل کاربر روی یک Command Button یا Combo Box.
- کد را از یک Form اجرا کنید. در فصل ۹ خواهید دید چگونه Formهای خود را تعریف کنید تا کاربر از آنها برای انتخاب گزینهها استفاده کند. با کلیک کاربر روی دکمهٔ OK، ماکرو اجرا میشود و ترجیحات انتخابشده را دریافت میکند.
- کد را از یک Procedure دیگر VBA فراخوانی کنید. کد میتواند به شکل Subroutine یا Function نوشته شود و بعد در سایر ماکروهای همان برنامه به کار رود. برای مثال، میتوانید Subroutineای بنویسید که یک رشتهٔ متن را برای یافتن کاراکتری مشخص جستوجو کند و رشته را بهصورت Parameter دریافت کند. سپس این Subroutine مانند یک بلوک سازنده از هر Procedure دیگر قابل فراخوانی است، تقریباً همانطور که یک Keyword عادی VBA را به کار میبرید.
- مستقیماً روی کد کلیک کرده و F5 را بزنید. این روش فقط برای توسعه مناسب است؛ مثلاً زمانی که Subroutineای را جداگانه میسازید و میخواهید صرفاً نحوهٔ کار آن را آزمایش کنید.
همهٔ این روشها در بخشهای بعدی کتاب با جزئیات بیشتر بررسی میشوند.
یک پروژهٔ VBA معمولاً دستکم یک Module برای نگهداری Functionها و Subroutineهای لازم، که مجموعاً Procedure نامیده میشوند، دارد. برای درج Module جدید در منوی VBE گزینهٔ Insert | Module را انتخاب کنید. Module جدید در ابتدا فقط یک ناحیهٔ General دارد و برخلاف صفحههای کد Form و Report هیچ Eventی ندارد.
در اینجا میتوانید Subroutine یا Function وارد کنید و آنها را Public یا Private قرار دهید. تفاوت Public و Private تعیین میکند آیا ماژولهای دیگر همان Database میتوانند به Procedure دسترسی داشته باشند یا نه. کد Private فقط در همان Module قابل استفاده است، در حالی که کد Public از هر Procedure در هر Module دیگر پایگاهداده قابل فراخوانی است. اگر Subroutineای دارید که نباید در بخشهای دیگر کد استفاده شود، آن را Private کنید. حالت پیشفرض Public است.
تفاوت Subroutine و Function
Subroutine و Function دو نوع Procedure کد هستند. در نگاه سطحی ممکن است یکسان به نظر برسند، اما در واقع تفاوت مهمی دارند.
Subroutine قطعه کدی است که مجموعهای از عملیات، محاسبات یا ترکیبی از هر دو را انجام میدهد. میتواند یک «بلوک سازنده» داخل برنامه باشد و گاهی لازم باشد بارها تکرار شود. چند Routine مختلف میتوانند همان Subroutine را فراخوانی کنند؛ برنامهنویس آن را فقط یک بار مینویسد و سپس هر تعداد بار از نقاط مختلف برنامه استفاده میکند. با این حال Subroutine مستقیماً مقدار بازگشتی ندارد. اگر محاسبهای انجام دهد، راه مستقیمی برای دریافت نتیجه وجود ندارد، هرچند با پارامترهای ByRef میتواند مقدار متغیرها را تغییر دهد؛ این روش در ادامهٔ فصل توضیح داده میشود. یک Subroutine را میتوان با دستور Call فراخوانی کرد:
Sub Main()
Call MySub 'Calls another macro/procedure called MySub
End Sub
استفاده از واژهٔ Call اجباری نیست و شکل زیر نیز کار میکند:
Sub Main()
MySub 'Calls another macro/procedure called MySub
End Sub
Function تقریباً همانند Subroutine است، با این تفاوت که یک مقدار برمیگرداند. Function با Function آغاز و با End Function پایان مییابد. به همین دلیل معمولاً باید آن را با یک متغیر فراخوانی کرد تا مقدار برگشتی را دریافت کند:
x=Now()
متغیر x مقدار تاریخ امروز را دریافت میکند. این نمونهای بسیار ساده از فراخوانی یک Function داخلی است.
هم Subroutine و هم Function میتوانند Parameter داشته باشند؛ مقادیر ارسالی داخل پرانتز قرار میگیرند.
نوشتن یک Subroutine ساده
Subroutine برخلاف Function چیزی را مستقیماً برنمیگرداند و بنابراین نمیتوان آن را دقیقاً مانند یک Function داخل Expression به کار برد. Subroutine معمولاً بلوکی از کد است که ممکن است بارها و از نقاط مختلف برنامه فراخوانی شود. هنگام فراخوانی، آدرس بازگشت ــ محلی که فراخوانی از آن انجام شده ــ ذخیره میشود. پس از پایان Subroutine، کنترل برنامه به همان آدرس بازمیگردد. میتوان Parameter نیز به آن فرستاد، اما این پارامترها در داخل خود کد استفاده میشوند.
به Module1 برگردید و کد زیر را اضافه کنید:
Sub Display(Target)
MsgBox Target
End Sub
این Subroutine یک Argument به نام Target دارد، زیرا قرار است از Procedure دیگری فراخوانی شده و یک متغیر به آن فرستاده شود.
برای جداکردن Subroutine جدید خطی رسم میشود و نام آن بهطور خودکار به فهرست کشویی گوشهٔ بالا-چپ اضافه میشود. اکنون به مثال اولیهٔ Hello World از فصل ۱ برگردید و کد را به شکل زیر تغییر دهید:
Sub MyCode()
'MsgBox "Hello World"
x = 3 * 5
MsgBox x
Call Display("my subroutine")
End Sub
مطمئن شوید Function/Procedureای با نام Display را از قبل تعریف کردهاید، وگرنه هنگام اجرای MyCode خطا دریافت خواهید کرد.
Cursor را داخل Procedure با نام MyCode قرار دهید و روی Run در Toolbar VBE کلیک کنید یا F5 را بزنید. ابتدا کادر پیامی با مقدار ۱۵ و سپس کادر پیامی با متن my subroutine خواهید دید.
فرمان Call، Subroutine را فراخوانی کرده و Parameterهای لازم را به آن میفرستد، سپس کد داخل Subroutine اجرا میشود و کنترل به دستور بعد از Call برمیگردد. در این مثال رشتهٔ my subroutine در متغیر Target قرار میگیرد.
اگر Subroutine شما Parameter ندارد، میتوانید آن را از صفحهٔ کد با انتخاب Run | Run Sub/UserForm، فشار دادن F5 یا کلیک روی نماد Run اجرا کنید. Cursor باید روی Subroutine موردنظر باشد. این روش برای آزمایش کد و یافتن Bugهای احتمالی بسیار مفید است.
Subroutineها کمک میکنند پروژههای بزرگ را به قطعات قابلمدیریت تقسیم کنید تا Routineهای بسیار بزرگ و دستوپاگیر ایجاد نشوند. شکستن مسئله به اجزای کوچک، کارکردن جداگانه روی هر بخش و اطمینان از صحت آن پیش از رفتن به بخش بعدی بسیار سادهتر از نوشتن یک تودهٔ بزرگ کد است؛ روشی که معمولاً باعث تکرار غیرضروری کد میشود.
نوشتن یک Function ساده
هدف این تمرین ساخت Functionای است که دو عدد را دریافت کند، آنها را در هم ضرب کند و نتیجه را برگرداند. نام Function، Multiply است. چهار عملگر ریاضی اصلی مورد استفاده در Functionها و Subroutineهای VBA عبارتاند از:
Function Multiply(a, b)
Multiply = a * b
End Function
نتیجه باید شبیه شکل ۳-۱ باشد. همانند Subroutine، حداقل باید خط Function و خط End Function، یعنی Header و Footer، وجود داشته باشد. این Function باید در یک Module معمولی که خودتان درج کردهاید نوشته شود، نه در Module متعلق به Form یا Report.
Header دو Parameter به نامهای a و b را در پرانتز پس از نام Function معرفی میکند و یک کاما دو Argument را جدا میکند. این Argumentها همان دو عددیاند که باید ضرب شوند و نامشان میتواند هر چیزی باشد، به شرطی که نام متغیر در سراسر Function یکسان باقی بماند.
نام Function یعنی Multiply بهعنوان متغیر بازگشتی استفاده میشود. این تنها روش بازگرداندن پاسخ به Routine فراخواننده است. نام Function اکنون در فهرست کشویی بالای پنجرهٔ کد نیز ظاهر میشود، زیرا به یک Function رسمی در کد VBA و Access تبدیل شده است.
این Function را میتوانید به دو روش استفاده کنید: مستقیماً از یک SQL Query یا از داخل کد VBA. برای فراخوانی مستقیم در SQL Query، متن منبع نمونهٔ select Multiply(3.4) from MyTable; را ارائه میکند. داشتن Functionهای سفارشی، امکان افزودن قابلیتهای تازهای به Queryها را فراهم میکند.
هنگام اجرای Queryهایی که Function سفارشی را روی حجم زیادی از داده فراخوانی میکنند، یک هشدار وجود دارد: پیش از اجرای Query پنجرهٔ VBE را ببندید؛ در غیر این صورت آن پنجره هنگام اجرای Query دائماً بهروزرسانی میشود و اجرای Query میتواند بسیار طولانیتر شود.
شکل ۳-۱ — ایجاد یک Function ساده برای ضرب
برای روش دوم، یعنی فراخوانی از VBA، به مثال اولیهٔ Hello World در فصل ۱ برگردید. عبارت Hello World را با قرار دادن تککوتیشن در ابتدای آن Comment کنید و کد زیر را وارد کنید:
Sub MyCode()
'MsgBox "Hello World"
x = Multiply(3, 5)
MsgBox x
End Sub
هنگام تایپ واژهٔ Multiply و بازکردن پرانتز، VBA بهطور خودکار نام Parameterهای مورد انتظار را نمایش میدهد. با قراردادن Function در کد، فراخوانی آن را با پارامترهای ۳ و ۵ انجام میدهید تا جای a و b در Function را بگیرند. نتیجه در متغیر x برگردانده میشود.
Cursor را داخل MyCode قرار دهید و کد را با Run یا F5 اجرا کنید؛ کادر پیام مقدار ۱۵ را نشان میدهد.
Functionها و Subroutineهای Public و Private
در VBA میتوانید با Keywordهای Public و Private Function یا Subroutine را عمومی یا خصوصی تعریف کنید:
Private Sub PrivateSub()
End Sub
تمام Subroutineها و Functionهایی که ایجاد میکنید بهطور پیشفرض Public هستند. یعنی در تمام Moduleهای برنامه قابل استفادهاند و کاربران پایگاهداده نیز میتوانند Subroutineهای عمومی را در کد VBA خود به کار ببرند. همچنین Public Functionهای کد شما در کد یا SQL Queryهای آنها قابل فراخوانی است.
استثنا، Form و Report است. همانطور که در فصل ۹ توضیح داده میشود، Formها Module خود را دارند و Reportها نیز همینطور. یک Subroutine یا Function عمومی روی Form از Moduleهای دیگر با ارجاع به شیء Form قابل فراخوانی است؛ مثلاً Form_Form1.MysubRoutine. اگر نام Form را مانند این مثال مشخص نکنید، یا Procedure خصوصی باشد، هنگام اجرا خطای Compile رخ خواهد داد.
با تعریف Private میتوانید Procedureهایی با نام یکسان در Moduleهای متفاوت داشته باشید. آن Procedure فقط در Module خودش دیده میشود و Moduleهای دیگر به آن دسترسی ندارند. مهمتر آنکه کاربر Access نیز نمیتواند آن را در SQL Query اجرا کند.
وجود نامهای یکسان میتواند هم برای برنامهنویس و هم برای VBA گیجکننده باشد. VBA برای انتخاب Procedure مجموعهای از قواعد دارد: ابتدا در Module فعلی که کد در آن اجرا میشود جستوجو میکند؛ اگر Procedure موردنظر را پیدا نکند، همهٔ Moduleها را میگردد. فراخوانیهای داخل Moduleای که Procedure خصوصی در آن تعریف شده به همان Procedure میروند، اما فراخوانیهای خارج از آن Module به Procedure عمومی هدایت میشوند.
نوع دادهٔ Argumentها
هنگام تعریف Argumentهای یک Procedure، نوع پیشفرض آنها Variant است. میتوانید Parameterها را با هر یک از انواع دادهٔ فصل قبل نیز تعریف کنید.
مزیت مشخصکردن نوع داده این است که انضباطی در کد ایجاد میکند و روشن میسازد Procedure چه نوع اطلاعاتی را انتظار دارد. اگر نوع مشخص نکنید و Variant پیشفرض را بپذیرید، Procedure هر چیزی، عدد یا String، را قبول میکند. این مسئله میتواند پیامد نامناسبی داشته باشد؛ مثلاً وقتی کد انتظار String دارد اما عدد دریافت میکند یا برعکس. اگر Parameter را String تعریف کنید و String نفرستید، خطا رخ میدهد:
Function Myfunction(Target as String)
این موضوع هنگام ساخت Function سفارشی برای SQL Query نیز مفید است. کاربری که Function شما را در Query قرار میدهد باید Parameterها را مطابق نوع دادهٔ مشخصشده وارد کند. اگر String تعیین شده باشد باید مقدار در Quote قرار گیرد، یا میتوان از [Input Value] بهعنوان Parameter برای دریافت ورودی کاربر استفاده کرد.
Argumentهای اختیاری
با واژهٔ کلیدی Optional میتوانید بعضی Argumentها را اختیاری کنید:
Function Myfunction (Target as String, Optional Flag as Integer)
در این مثال کاربر باید Parameter با نام Target را بهصورت String بدهد، اما Flag در نمایش پارامترها داخل براکت قرار میگیرد و لازم نیست مشخص شود. همهٔ Parameterهای Optional باید بعد از Parameterهای اجباری قرار بگیرند.
ارسال Argument با Value یا Reference
با Keywordهای ByVal و ByRef مشخص میکنید Parameter چگونه به Procedure ارسال شود. تفاوت مهم این است که ByVal یک کپی از متغیر را میفرستد و مقدار اصلی متغیر را در Subroutine یا Function تغییر نمیدهد. ByRef خود متغیر اصلی را به Procedure میدهد و هر تغییری که در Subroutine یا Function روی مقدار انجام شود در تمام دامنهٔ آن متغیر منعکس میشود.
روش ByRef میتواند Bugهایی ایجاد کند که ردیابیشان دشوار است. Function ممکن است مقدار متغیر را تغییر دهد، اما وقتی زنجیرهٔ کد را بررسی میکنید این تغییر بهسادگی قابل مشاهده نباشد. بهطور کلی برنامهنویسان استفاده از ByRef را توصیه نمیکنند مگر آنکه دقیقاً بدانید چه میکنید.
Function MyFunction (ByVal Target as String)
Keyword ByVal تضمین میکند Parameter بهصورت مقدار ارسال شود، نه Reference به متغیر. ارسال Reference میتواند بهسادگی Bug را وارد کد کند. برای نمونه اگر متغیر با Reference ارسال شود:
x = 100
z = Adjust(x)
Function Adjust(ByRef Target as Integer)
متغیر x تحت تأثیر تغییراتی قرار میگیرد که در Function با نام Adjust روی Target انجام میشود. اما اگر مقدار را با ByVal ارسال کنید، فقط یک کپی از متغیر فرستاده میشود:
x = 100
z = Adjust(x)
Function Adjust(ByVal Target as Integer)
اگر Procedure مقدار Target را تغییر دهد، تغییر فقط روی کپی اعمال میشود و خود x همیشه مقدار ۱۰۰ را حفظ میکند.
معمولاً انتظار ندارید Function یک Argument را تغییر دهد، اما چنین اتفاقی ممکن است رخ دهد و Bugهایی ایجاد کند که یافتنشان دشوار است. گاهی عمداً میخواهید Function مقدار یک متغیر را تغییر دهد و آن زمان ByRef مناسب است، اما در بیشتر موارد ByVal استفاده میشود.
فصل ۴: مبانی برنامهنویسی — تصمیمگیری و حلقهها
هنگام نوشتن برنامه باید بدانید برنامه چگونه تصمیم میگیرد و چگونه Loop اجرا میکند. Looping یعنی تکرار مجموعهای یکسان از دستورها تا زمانی که شرطهای معینی برقرار شوند.
همهٔ ما هر روز تصمیم میگیریم؛ مثلاً صبح که بیدار میشویم تصمیم میگیریم کدام پیراهن را بپوشیم. این تصمیم بر اساس واقعیتهایی مثل وضعیت هوا و کاری که در آن روز داریم گرفته میشود. زندگی بدون تصمیم بسیار یکنواخت بود.
برنامهها نیز باید بر اساس Parameterها و دادههایی که در اختیار دارند تصمیم بگیرند. برای مثال، اگر برنامه تلاش کند فایل متنی را بارگذاری کند و فایل پیدا نشود، باید تصمیم بگیرد چه کند: فقط پیام خطا نشان دهد و متوقف شود یا با هوشمندی بیشتر کاربر را از نبود فایل آگاه کند و راهکار جایگزین ارائه دهد؟
هوش مصنوعی موضوعی است که در محافل رایانهای زیاد مطرح میشود. وقتی برنامه را وادار میکنید تصمیم بگیرد، شکل ابتداییای از هوشمندی را در آن قرار میدهید؛ البته این هوشمندی در اصل همان منطق شماست که در کد نوشته شده و به برنامه میگوید در شرایط متفاوت چه کاری انجام دهد. در عمل مجموعهای از قواعد برای برخورد با موقعیتهای مختلف مینویسید.
Looping نیز کاری است که انسان هر روز بدون فکرکردن انجام میدهد. هنگام غذاخوردن یک چرخهٔ مشابه را بارها تکرار میکنید: غذا را از بشقاب برمیدارید و در دهان میگذارید. برنامههای رایانهای نیز اغلب یک قطعه کد را چندین بار تکرار میکنند تا شرط مشخصی برقرار شود.
تصمیمگیری
برنامهها، مگر بسیار ساده باشند، معمولاً بر اساس دادهٔ دریافتشده یا ورودی کاربر تصمیم میگیرند. تصمیمگیری یکی از مهمترین بخشهای برنامهنویسی است، زیرا تعیین میکند هنگام رخدادن Eventهای مختلف چه اتفاقی بیفتد.
نمونهٔ رایج: اگر چیزی درست بود، عمل اول را انجام بده، وگرنه عمل دوم. در زندگی روزمره میتوان گفت: «اگر باران میبارد، چتر بردار؛ در غیر این صورت عینک آفتابی همراه داشته باش.»
کد زیر نشان میدهد عبارت شرطی If..Then..Else چگونه کار میکند:
Sub Test_If()
If CurrentDb.Name= "C:\Temp\MyDB.accdb" Then
MsgBox "Database path and name are correct"
Else
MsgBox "Database has been re-named/moved"
End If
End Sub
عبارت CurrentDb.Name نام پایگاهدادهٔ جاری را برمیگرداند؛ در Access شامل مسیر کامل و نام فایل است. کد را اجرا کنید و مسیر C:\Temp\MyDB.accdb را با مسیر و نام فایل خودتان جایگزین کنید. پیام صحیحبودن مسیر و نام پایگاهداده نمایش داده خواهد شد.
کد را ذخیره کنید، در Windows Explorer از فایل پایگاهداده کپی بگیرید، نسخهٔ جدید را باز کنید و دوباره کد را اجرا کنید. این بار پیام تغییر نام یا جابهجایی پایگاهداده را خواهید دید.
همین قطعه کد ساده بررسی میکند پایگاهداده از کجا بارگذاری شده و آیا نام آن تغییر کرده است. شاید ساده به نظر برسد، اما گاهی صحت برنامه به درستبودن همین اطلاعات وابسته است.
در شکل ۴-۱ کد برای جداکردن قسمتهای اصلی شرط تورفتگی دارد. این کار خواندن و Debug کردن را سادهتر میکند، چون بلافاصله میتوان گروه دستورها و شروع و پایان آنها را دید. Ifها میتوانند داخل یکدیگر Nest شوند؛ یعنی یک If درون If دیگر قرار بگیرد. در برنامههای پیچیده این اتفاق زیاد رخ میدهد و داشتن تورفتگی مانع گمشدن در ساختار شرطها میشود.
دستور End If پایان شرط را مشخص میکند. همچنین میتوان If را روی یک خط نوشت که دیگر End If لازم ندارد:
If CurrentDb.Name= "C:\Temp\MyDB.accdb" Then MsgBox _
"Database path and name are correct"
در این مثال برای ادامهدادن دستور روی خط بعد از کاراکتر ادامه، یعنی Space و Underscore، استفاده شده است. کاراکتر ادامه را نمیتوان داخل Quote و بهعنوان بخشی از String قرار داد.
اگر چند دستور باید اجرا شوند، میتوانید همه را با Colon در یک خط از هم جدا کنید؛ اما چنین کدی بهسرعت دشوارخوان و دشوار برای Debug میشود و بسیاری مواقع تعداد دستورها آنقدر زیاد است که قراردادن همه در یک خط منطقی نیست.
شکل ۴-۱ — کد یک عبارت شرطی If
عملگرهای شرطی قابل استفاده عبارتاند از:
| عملگر | معنا |
| = | دو عدد یا مقدار برابرند؛ برای مقادیر متنی مانند dog و cat نیز قابل استفاده است. |
| < | مقدار اول از مقدار دوم کوچکتر است. |
| > | مقدار اول از مقدار دوم بزرگتر است. |
| <= | مقدار اول کوچکتر یا مساوی مقدار دوم است. |
| >= | مقدار اول بزرگتر یا مساوی مقدار دوم است. |
| <> | دو مقدار نابرابرند. |
Expressionای مانند x=1 به یک مقدار Boolean یعنی True/False یا Non-zero/Zero ارزیابی میشود. بنابراین همیشه لازم نیست عملگر مقایسه بنویسید. اگر فقط میخواهید بدانید متغیر مقدار غیرصفر دارد، میتوانید بنویسید:
If MyVar Then MsgBox "MyVar has a value"
چند شرط همزمان
در مثالهای قبل فقط یک شرط مانند If x=1 Then وجود داشت. میتوان چند شرط را با عملگرهای منطقی ترکیب کرد. فصل ۶ دربارهٔ Logical Operatorها توضیح بیشتری میدهد.
ترکیب شرطها تقریباً مانند زبان عادی با And و Or انجام میشود:
If x = 1 And y > 5 Then
MsgBox "x=1 and y>5"
End If
کادر پیام فقط زمانی نمایش داده میشود که هر دو شرط برقرار باشند. اگر x بزرگتر از ۱ باشد اما y برابر ۴ باشد، پیام نمایش داده نمیشود.
If x = 1 Or y > 5 Then
MsgBox "x=1 or y>5"
End If
در حالت Or کافی است یکی از شرطها برقرار باشد. بنابراین اگر x برابر ۰ و y برابر ۶ باشد، یا x برابر ۱ و y برابر ۴، در هر دو حالت پیام ظاهر میشود.
میتوانید چند And یا Or در یک شرط قرار دهید، هرچند با بیش از سه مورد ساختار پیچیده میشود. پیچیدگی به هدف تصمیم و کاری که Procedure باید انجام دهد وابسته است؛ گاهی شرط سادهای مثل If x=1 Then کافی است و گاهی منطق پیچیدهتری لازم میشود.
دستور Select Case
برای پردازش شرطی میتوان از Select Case نیز استفاده کرد. اگر متغیری دارید و میخواهید بر اساس مقدارش عملیات متفاوتی انجام شود، میتوانید چند If بنویسید:
If x=1 then MsgBox "x=1"
If x=2 then MsgBox "x=2"
If x=3 then MsgBox "x=3"
اما Select Case کد را بسیار تمیزتر میکند:
x = 23
Select Case (x)
Case (1)
MsgBox "x=1"
Case (23)
MsgBox "x=23"
End Select
Select Case (x) متغیر x را بهعنوان مقدار مورد بررسی تعیین میکند. Case (1) عملی را مشخص میکند که اگر x برابر ۱ باشد انجام میشود و Case (23) حالت x برابر ۲۳ را تعیین میکند. چون در ابتدای کد x برابر ۲۳ قرار داده شده، پیام x=23 نمایش داده میشود.
در Case میتوان از To و Is نیز استفاده کرد:
Sub Test_Case (Grade)
Select Case Grade
Case 1
Msgbox "Grade 1"
Case 2, 3
Msgbox "Grade 2 or 3"
Case 4 To 6
Msgbox "Grade 4, 5 or 6"
Case Is > 8
MsgBox "Grade is above 8"
Case Else
Msgbox "Grade not in conditional statements"
End Select
End Sub
حلقهها
بدون قابلیت Loop، برنامهها بسیار خستهکننده و نگهداری آنها دشوار بود. Looping اجازه میدهد یک بلوک کد تا زمانی که شرط یا مقدار مشخصی برقرار شود تکرار گردد. برای نمونه اگر بخواهید اعداد ۱ تا ۵ را نمایش دهید، میتوانید پنج MsgBox جداگانه بنویسید، اما این روش ناکارآمد است. برای نمایش اعداد تا ۱۰۰۰ مجبور بودید ۹۹۵ خط دیگر اضافه کنید.
حلقههای For..Next
For n = 1 to 5
MsgBox n
Next n
کادر پیام پنج بار ظاهر میشود و مقادیر n از ۱ تا ۵ را نمایش میدهد. نام متغیر میتواند هر چیزی باشد، اما در تمام حلقه باید یکسان بماند. نمیتوانید حلقه را با n آغاز کنید و با Next m پایان دهید. همچنین نام متغیر نباید Reserved Word باشد. میان For و Next هر تعداد دستور لازم میتوان قرار داد و حتی Subroutine یا Function فراخوانی کرد. مقدار آغاز و پایان نیز آزاد است و لازم نیست ۱ و ۵ باشد.
گزینهٔ Step امکان تعیین اندازه و جهت افزایش Index را فراهم میکند. حالت پیشفرض افزایش یکواحدی است:
For n = 3 to 12 Step 3
MsgBox n
Next n
نتیجه ۳، ۶، ۹ و ۱۲ است.
For n= 10 to 1 Step -1
MsgBox n
Next n
این بار نتیجه از ۱۰ تا ۱ بهصورت نزولی خواهد بود.
حلقههای For..Next میتوانند داخل هم Nest شوند. برای مثال برای پیمایش مقادیر یک آرایهٔ چندبعدی میتوان یک For..Next را برای یک بُعد و حلقهٔ دیگری را برای بُعد دوم استفاده کرد.
Sub test_loop()
For n = 1 To 4
For m = 1 To 5
MsgBox "n= " & n
MsgBox "m= " & m
Next m
Next n
End Sub
تورفتگی کد Nested بودن حلقهها را روشن میکند. حلقهٔ m داخل حلقهٔ n قرار دارد؛ بنابراین برای اولین مقدار n همهٔ مقادیر m پردازش میشوند، سپس مقدار بعدی n و دوباره تمام mها. این تورفتگی کمک میکند حتی وقتی ماهها بعد به کد برمیگردید ساختار آن را گم نکنید.
حلقههای For Each
For Each بسیار شبیه For..Next است، اما بهطور خاص برای Collectionها و Arrayها به کار میرود. For Each بدون Index صریح، تکتک آیتمها را طی میکند. این ویژگی هنگام جستوجوی یک Collection و حذف یک Object مفید است، زیرا موقعیت Collection پس از حذف بهدرستی در Loop حفظ میشود. اگر با For..Next و Index شیئی را حذف کنید، Index جابهجا میشود و Routine ممکن است یک دور اضافه اجرا کند و خطا بدهد.
مثال زیر نام همهٔ Queryهای پایگاهدادهٔ جاری را پیمایش میکند:
Sub ShowName()
Dim oQry As QueryDef
For Each oQry In CurrentDb.QueryDefs
MsgBox oQry.Name
Next oQry
End Sub
حلقههای Do Until
حلقهٔ Do Until تا زمانی تکرار میشود که شرط مشخصی برقرار شود. اغلب این یعنی منتظر میماند تا متغیری به مقدار خاصی برسد. با برقرارشدن شرط، Loop پایان مییابد و برنامه از دستور بعد از حلقه ادامه پیدا میکند. همچنین میتوان از While استفاده کرد تا تا زمانی که یک شرط برقرار است تکرار ادامه یابد.
Sub test_do()
x = 0
Do Until x = 100
x = x + 1
Loop
MsgBox x
End Sub
ابتدا x برابر صفر میشود. شرط توقف حلقه x=100 است. در هر دور x یک واحد افزایش مییابد و حلقه ۱۰۰ بار تکرار میشود. سپس MsgBox مقدار ۱۰۰ را نشان میدهد.
حلقههای While..Wend
While..Wend تا زمانی که شرط تعیینشده True باشد ادامه پیدا میکند و بهمحض False شدن شرط متوقف میشود:
Sub test_do()
x = 0
While x < 50
x = x + 1
Wend
MsgBox x
End Sub
در این مثال x از صفر آغاز میشود و تا زمانی که کمتر از ۵۰ است در هر دور یک واحد افزایش پیدا میکند. هنگامی که x به ۵۰ میرسد دیگر شرط درست نیست و کادر پیام مقدار ۵۰ را نمایش میدهد.
خروج زودهنگام از حلقهها
گاهی میخواهید پیش از رسیدن حلقه به شرط پایانی از آن خارج شوید. برای مثال ممکن است یک رشته را در آرایهای جستوجو کنید. اگر ۲۵ مورد برای بررسی وجود دارد ولی مورد مطلوب در همان ابتدای کار پیدا شده است، ادامهٔ جستوجو تا انتها فایدهای ندارد. در آرایهای با چند هزار رکورد این کار زمان زیادی هدر میدهد. در For..Next مقدار Index هنگام خروج حفظ میشود و میتوانید از آن برای یافتن محل برقراری شرط استفاده کنید.
Sub test_exit()
For x = 1 To 100
If x = 50 Then
Exit For
End If
Next x
MsgBox x
End Sub
برای خروج از For..Next یا For Each از Exit For و برای خروج از Do Until از Exit Do استفاده میشود. اگر Loopها Nested باشند، Exit فقط از همان حلقهای خارج میشود که دستور داخل آن قرار دارد؛ برای خروج از حلقهٔ بیرونی باید Exit جداگانهای در آن قرار دهید. Exit Do و Exit For اجرای حلقه را متوقف کرده و برنامه را از اولین دستور بعد از پایان حلقه ادامه میدهند.