آموزش ++C – تابع

بدون دیدگاه

در این قسمت می‌خواهیم به مفهوم تابع و نحوه پیاده‌سازی آن بپردازیم. بد نیست ابتدا در مورد این موضوع صحبت کنیم که چرا به تابع نیاز داریم؟
با یک مثال بحثمان را جلو می‌بریم. در مثال ۴ از آموزش ساختار تکرار کد مربوط به محاسبه فاکتوریل یک عدد را نوشتیم. حال فرض کنید که مساله به این صورت باشد:
برنامه ای بنویسید که از ورودی n و k را دریافت کند و ترکیب k از n را محاسبه کند

شما اگر الگوریتم محاسبه فاکتوریل را بلد باشید نوشتن ترکیب k از n سخت به نظر نمی‌رسد. کافی است که فاکتوریل اعداد n، k و (n-k) را کدنویسی کنید. اما همانطور که در ذهنتان کد این برنامه را مرور می‌کنید خواهید فهمید که شما الگوریتم فاکتوریل را باید سه بار (و برای سه عدد مختلف) بازنویسی کنید.اینجاست که شما به مفهوم تابع نیاز پیدا میکنید.شما می‌توانید الگوریتم مربوط به فاکتوریل را در تابعی با همین نام (مثلا تابع fact) کدنویسی کنید و این تابع به ازای ورودی های متفاوت فراخوانی کنید. مثلا fact(3) عدد ۶ را محاسبه می‌کند و fact(5) عدد ۱۲۰.

اگر به یاد داشته باشید قبلا از توابع pow و sqrt استفاده کرده‌ایم. در c++ توابع آماده ای وجود دارند که شما نیازی به دانستن نحوه پیاده سازی آنها ندارید. فقط کافیست الگوی تابع را بلد باشید به این معنی این  که نام تابع را بلد باشید، بدانید چه ورودی هایی دارند و خروجی آن به چه صورت است.

تعریف الگوی تابع

برای تعریف و پیاده سازی تابع باید با الگوی تابع آشنا شوید. در الگوی تابع هر آنچه که در مورد کار کردن با یک تابع نیاز است بیان می‌شود. برای درک بهتر موضوع با مثال تابع فاکتوریل ادامه می‌دهیم. الگوی تابع فاکتوریل به صورت زیر است:

اطلاعاتی که از این الگو بدست می‌آید این است که تابعی به نام fact داریم که یک ورودی از نوع عدد صحیح (int) دریافت می‌کند، فاکتوریل آن را محاسبه می‌کند و خروجی که برمی‌گرداند از جنس عدد صحیح (int) است. پس الگوی تابع به صورت زیر است:

        (پارامترهای ورودی) نام تابع نوع برگشتی

برای درک بهتر موضوع الگوی توابع pow و sqrt را ببینید:

همانطور که در الگوی تابع pow می‌بینید یک تابع می‌تواند بیشتر از یک ورودی داشته باشد.

پیاده سازی تابع:

برای پیاده سازی تابع ابتدا باید الگوی تابع را قبل از int main() نوشت. توجه داشته باشید که در انتهای الگو سیمی کالن فراموش نکنید. پیاده سازی تابع نیز باید بعد از int main(){  }  نوشت. بهتر است توضیحات بیشتر را در مثال زیر دنبال کنید:

مثال ۱:  برنامه ای بنویسید که از ورودی n و k را دریافت کند و ترکیب k از n را محاسبه کند:

توضیح کد:
۱- همانگونه که قبلا توضیح داده شد در خط ۶ الگوی تابع معرفی شده است.
۲- در خط ۱۴ سه بار تابع fact فراخوانی شده است. در مورد فراخوانی تابع صحبت خواهد شد.
۳- در خط ۱۸ تا ۲۴ پیاده‌سازی تابع صورت گرفته است. با دقت در نحوه پیاده سازی می‌بینید که در خط اول پیاده سازی همان الگوی تابع (که در خط ۶ نوشته شده است) نوشته شده و به جای سیمی کالن، آکولاد باز و دستوارت تابع نوشته شده است.

نگاهی دقیق تر به پیاده سازی و فراخوانی تابع
بیایید نگاهی دقیق تر به این مثال داشته باشیم. در خط ۶ الگوی تابع معرفی شده است. در خط ۱۲ از ورودی n و k دریافت می‌شود و در خطا ۱۴ سه بار تابع fact فراخوانی می‌شود. فراخوانی تابع به این معنی است که یک (یا چند) ورودی مشخص به تابع ارسال میکنیم. تابع بر اساس ورودی دستورات را اجرا می‌کند و در نهایت مقدار محاسبه شده را برمیگرداند. مثلا فرض کنید n=5 باشد. در فراخوانی fact(n) (در خط ۱۴) عدد ۵ به تابع فاکتوریل در ۱۸ ارسال می‌شود. تابع fact در خط ۱۸، عدد ۵ را در متغیر x دریافت می‌کند، فاکتوریل آن را در f ذخیره می‌کند و در نهایت مقدار f (که ۱۲۰ است) را به جایی برمیگرداند که این تابع فراخوانی شده است. (یعنی fact(n) در خط ۱۴) همین فرایند برای fact(k) و fact(n-k) نیز تکرار می‌شود.

نکات:
۱- تعداد و نوع پارامترها در الگوی تابع، فراخوانی تابع و پیاده سازی تابع باید مثل هم باشند. در تصویر بالا این موارد با هایلایت صورتی رنگ مشخص شده اند.
۲- در پیاده‌سازی تابع نوع برگشتی و مقداری که return می‌شود باید از یک نوع باشد. این مورد در تصویر بالا با هایلایت سبز رنگ مشخص شده است. نوع برگشتی تابع fact از جنس int است و متغیر f که برگشت داده می‌شود نیز از جنس int است.
۳- اگر تابع نیاز به برگشت مقداری نداشته باشد نوع برگشتی را void در نظر میگیریم. این نوعِ برگشتی را در مثالهای بعدی خواهید دید.

مثال ۲- برنامه‌ای بنویسید که اعداد اول کوچکتر از ۱۰۰ را چاپ کند.

در مثال ۸ ساختار تکرار الگوریتم تشخیص عدد اول به صورت کامل توضیح داده شده است. در این مثال باید تابعی بنویسیم که یک عدد دریافت کنید و تشخیص دهد اول است یا خیر. خروجی این تابع از جنس bool است زیرا برای هر عدد دریافتی مشخص خواهد کرد که عدد اول است یا خیر، پس خروجی یا true است یا false.

توضیحات کد:
۱- در خط ۵ الگوی تابع نوشته شده است. این الگو نشان می‌دهد که نام تابع isPrime است، یک ورودی از جنس int دریافت می‌کند و در نهایت یا true و یا false برمیگرداند. به این معنی که عدد ورودی یا اول است یا خیر.
۲- در خط ۹ تا ۱۱ همه اعداد ۲ تا ۹۹ تولید می‌شود و با چنانچه فراخوانی تابع isPime(i) مقدار true برگرداند به این معنی است که i اول است و بنابراین در خروجی چاپ می‌شود.
۳- در خط ۱۶ تا ۲۲ پیاده‌سازی تابع isPrime نوشته شده است که کمی با روشی که قبلا حل شده فرق دارد. در این تابع عدد n دریافت می‌شود و چنانچه تشخیص دهد عدد اول است مقدار false را برمیگرداند (خط ۲۰) به این نکته توجه داشته باشید پس از اجرای خط ۲۰ مقدار false برگشت داده می‌شود (به کجا؟ جاییکه فراخوانی شده، یعنی خط ۱۰) و کلا از پیاده سازی تابع fact خارج می‌شویم. یعنی بقیه دستورات تابع اجرا نمی‌شود.
۴- طبق قضیه ای در ریاضی برای تشخیص اول بودن یک عدد نیاز نیست همه اعداد کوچکتر از عدد را بررسی کنیم. اعداد کوچکتر از رادیکال آن عدد بررسی شود کفایت می‌کند.

مثال ۳: برنامه ای‌ بنویسید که دو نقطه در صفحه مختصات از ورودی دریافت کند و فاصله این دو نقطه را چاپ کند.

برای دریافت دو نقطه به ۴ ورودی نیاز داریم. هر نقطه یک x و یک y دارد. بدیهی است که همه برنامه ها را می‌توان بدون استفاده از تابع پیاده سازی کرد ولی توجه داشته باشید که استفاده از تابع موجب کاهش پیچیدگی و کدنویسی و همچنین افزایش خوانایی برنامه می‌شود.

مثال ۴: فلوچارت برنامه‌ای بنویسید که ۱۳۹۸مین عدد اول را چاپ کند.

توضیحات کد:
در این مثال هم از تابع isPrime استفاده شده است. یکی از مزایای کار با توابع همین است. یکبار می‌نویسید و بارها استفاده می‌کنید!
با استفاده از متغیر i یکی یکی اعداد تولید می‌شوند و در خط ۱۱ چک می‌شوند که آیا عدد اول است یا خیر. اگر اول بود به counter یک واحد اضافه می‌شود. بنابراین تا زمانی که counter به ۱۳۹۸ نرسد ساختار تکرار باید تکرار شود. نکته مهم این است که شما در نهایت i-1 را در خروجی چاپ می‌کنید (خط ۱۶) چرا؟ زیرا وقتی که در خط ۱۱ ، ۱۳۹۸ امین عدد اول پیدا می‌شود (که همان i است) و یک واحد به counter اضافه میشود، مقدار counter برابر ۱۳۹۸ می‌شود و در خط ۱۳ یک واحد به i اضافه می‌شود. بنابراین بعد از خروج از ساختار تکرار باید i-1 چاپ شود.


مثال ۵- برنامه ای که زمان را بر حسب ثانیه دریافت کند، توسط یک تابع آن را تبدیل به ساعت، دقیقه و ثانیه کند و در خروجی نمایش دهد.

توضیح کد:
همانطور که در الگوی تابع convert میبینید نوع برگشتی این تابع از نوع void است. وقتی که یک تابع مقداری را برنگرداند (یعنی دستور return نداشته باشد) نوع بازگشتی را از جنس void تعریف می‌کنیم.

ارسال پارامتر به تابع

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

در تابع plus، از ورودی a و b را دریافت می‌کند و به هر کدام یک واحد اضافه می‌شود. با فرض مقدار اولیه ۵ برای a و b (خط ۷) و فراخوانی تابع در خط ۸ انتظار بی جایی است که فکر کنید در خط ۹ مقدار ۶ برای a و b چاپ شود. چرا؟ چون ما از روش ارسال پارامتر با مقدار استفاده کردیم. یعنی در ارسال پارامتر به تابع plus یک کپی از a و b به این تابع ارسال می‌شود و هر تغییری که بر روی این کپی انجام شود هیچ تاثیری بر روی مقادیر a و b موجود در int main ندارد. در مبحث اشاره گرها و با معرفی ارسال پارامتر با آدرس بیشتر توضیح می‌دهیم.

  • نویسنده
    حمید جهانگیری
  • تعداد بازدید
    729
۰دیدگاه فرستاده شده است.
شما هم دیدگاه خود را بنویسید
نوشته‌های ویژه
اخبار ویژه

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