توابع در Matlab (بخش سوم)
در بخش اول و دوم از سری مقالات تعریف تابع در متلب، با تابع inline و توابع ناشناس آشنا شدیم. در دو روش گفته شده نیازی به تعریف تابع در m-file جداگانه نیست. به راحتی میتوان در هر بخشی آن ها را تعریف کرد. امّا چنانچه کاربری قصد داشته باشد برنامهای طولانی بنویسد، قطعاً رفع خطا کار دشوار و زمانبری خواهد شد. بهترین راه حل استفاده از یک فایل اصلی و چندین زیرشاخه میباشد. مسئله در یک فایل اصلی، پیکر بندی میگردد. سپس بخش های فرعی بعنوان توابعی جدا تعریف شده و در فایل اصلی فراخوانی میشوند. در این روش هر تابع بصورت جداگانه بررسی و عیب یابی میشود.
تعریف تابع در m-file
تعریف تابع در این روش به صورت زیر است:
(function [outputs]= name(inputs
کلمه function در اول عبارت همیشه ثابت است. این عنوان همانند for و while در حلقه های کنترلی میباشد. در داخل علامت براکت ([ ]) متغیرهای خروجی نام برده میشوند. این متغیرها در واقع خواسته های ما از تابع است. گاهی هم این بخش خالی است و تابع بدون خروجی میباشد. بجای name هم نامی برای تابع در نظر گرفته میشود. این نام از این جهت اهمیت دارد که تابع با آن فراخوانی میشود. نامگذاری صحیح با یکی از حروف الفبا شروع میشود. پس از آن مجاز به استفاده از اعداد هستیم. دقت داشته باشید استفاده از عملوندهای ریاضی مثل (+ – * /) محاز نیست. همچنین استفاده از اعداد برای نامگذاری در ابتدای اسم موجب بروز خطا میشود. همچنین اگر کل نام تابع از اعداد باشد، در هنگام فراخوانی تابعی شناخته نشده و فقط یک مقدار اسکالر توسط نرم افزار شناسایی میشود. چنانچه عنوان یک تابع شامل چند کلمه باشد، آنگاه برای جداسازی کلمات از علامت underline ( _ ) استفاده میشود.
به مثال زیر توجه کنید.
function z=avg_num(a,b) z=(a+b)/2;
در مثال بالا تابع avg_num اسمی دو بخشی دارد که با علامت “_” از هم جدا شدهاند. این تابع دو مقدار a و b را دریافت میکند. سپس z را برمیگرداند. برای استفاده از این تابع ابتدا باید آن را با همان نام avg_num در پوشه ای ذخیره کنیم. پس از ذخیره سازی، بصورت زیر فراخوانی میشود.
تابع با خروجی های چندگانه
چنانچه تابع دو یا چند خروجی را برگرداند باید حتماً بعد از کلمه function پارامترها را به ترتیب در براکت قرار دهیم.
function [m,s] = stat(x) n = length(x); m = sum(x)/n; s = sqrt(sum((x-m).^2/n)); en
برای ذخیره مقادیر خروجی به شکل زیر عمل میکنیم.
values = [12.7, 45.4, 98.9, 26.6, 53.1]; [ave,stdev] = stat(values)
در مثال بالا ورودی بردار است و مقادیر محاسبه شده نیز هر کدام در متغیرهای ave و stdev ذخیره میشوند. دو متغیر نام برده نیز باید در داخل براکت قرار داشته باشند.
توابع چندگانه در یک فایل
توابع چندگانه به دو صورت پدید میآیند:
الف) توابع محلی (local)
به مثال زیر توجه کنید.
function [m,s] = stat2(x) n = length(x); m = avg(x,n); s = sqrt(sum((x-m).^2/n)); end function m = avg(x,n) m = sum(x)/n; end
این مثال که با نام stat2 ذخیره شده است، روشی دیگر برای پیاده سازی مثال قبلی میباشد. با این تفاوت که در اینجا از دو تابع استفاده شد. در خط سوم از کد stat2 ،تابع avg فراخوانی شد. برای تعیین تابع avg آن را پس از پایان تابع اول مینویسیم. لازم به ذکر است که توابع محلی مانند avg در این مثال به همراه تابع اصلی خودش ذخیره میشود.
در مقیاس های بزرگتر طبیعیست که با افزایش حجم کاری تعیین ارتباط میان توابع کمی دشوار شود. برای اینکه بتوانیم رابطهی توابع را باهم به راحتی پیدا کنیم، کافیست پس از تعریف تابع محلی توضیحی برای آن بنویسیم.
به متن تابع محلی دقت شود.
function m = avg(x,n) % AVG Example of a local function. m = sum(x)/n; end
جهت نوشتن توضیحات ابتدا از علامت (%) استفاده میشود تا دستور به شکل Comment تعریف شود. در ابتدای جمله، اسم تابع محلی آورده میشود. سپس توضیحی برای آن مینویسیم. حال برای تعیین رابطه بین تابع اصلی و محلی از دستور Help استفاده میکنیم.
help stat2>avg
نتیجه بصورت زیر خواهد بود.
ب) توابع تو در تو (Nested)
در این توابع دو بخش اصلی وجود دارد، تابع پدر (اصلی) و تابع فرزند (فرعی). تابع پدر تابعی است که در بالادست قرار دارد و ذخیره سازی تابع به نام آن صورت میگیرد.
چند نکته مهم :
- متغیرهایی که بعنوان ورودی در این تابع پذیرفته یا تولید میشود، برای تمام فرزندان قابل دسترسی و تغییر است.
- نمیتوان یک تابع فرزند را در عبارتهای کنترلی برنامه مانند if / elseif / else، switch / case، for، while، یا try / catch تعریف کرد.
- تابع فرزند باید به طور مستقیم با نام خودش (بدون استفاده از feval)، و یا با استفاده از یک Handle تابع که با استفاده از اپراتور @ ایجاد میشود (و نه str2func) فراخوانی شود.
- متغیرهایی که در تابع فرزند تعیین میشود فقط برای خودش و تابع پدر قابل تشخیص است.
دادهها و توابع تودرتو
همانطور که در نکات ذکر شد،بدون انتقال آرگومان توابع پدر و فرزند به متغیرهای یکدیگر دسترسی دارند. برای مثال، در هر یک از این توابع، main1 و main2، هر دو تابع اصلی و تابع فرزند میتوانند به متغیر x دسترسی پیدا کنند:
function main1 x = 5; nestfun1 function nestfun1 x = x + 1; end end
function main2 nestfun2 function nestfun2 x = 5; end x = x + 1; end
در main1 تابع فرزند به متغیر x دسترسی دارد و بالعکس در main2 تابع پدر به x دسترسی دارد.
به مثال زیر دقت کنید.
function main nestedfun1 nestedfun2 function nestedfun1 x = 1; end function nestedfun2 x = 2; end end
در مثال بالا متغیر x برای تابع پدر تعریف نشد. بنابراین هرکدام از فرزندان از متغیر محلی خود استفاده مینماید و شناخته شدن متغیر x برای تابع پدر در این حالت وابسته به فرزندان است.
بطور کلی توابع تودرتو می توانند متغیرها را از سه منبع استفاده کنند:
- آرگومان ورودی
- متغیرهای تعریف شده در تابع تو در تو
- متغیرهای تعریف شده در یک تابع پدر ، که متغیرهای دامنهی خارجی (externally scoped variables) نامیده می شوند.
هنگامی که Handle تابع برای تابع تو در تو ایجاد میشود، این Handle نه تنها نام تابع را ذخیره می کند، بلکه مقادیر متغیرهای خارجی را نیز ذخیره می کند. برای مثال، یک تابع در یک فایل با نام makeParabola.m ایجاد میکنیم. این تابع ضرایب یک چندجملهای را میپذیرد.سپس یک handle را به تابع تودرتو که مقدار چند را محاسبه میکند،برمیگرداند.
function p = makeParabola(a,b,c) p = @parabola; function y = parabola(x) y = a*x.^2 + b*x + c; end end
برای استفاده از تابع به شکل زیر عمل میکنیم.
p = makeParabola(1.3,.2,30); X = 25; Y = p(X)
رابطه توابع تودرتو باهم
همانطور که بیان شد توابع تودرتو شامل یک پدر و فرزندهای متعددی است. حال آنکه هر فرزند نیز میتواند مجموعهای از توابع دیگر را در بر گیرد. امّا رابطه این توابع با یکدیگر چگونه است؟ آیا همه ای توابع از دادهها و پارامترهای یکدیگر میتوانند استفاده کند؟ بطور کلی سطح دسترسی هر تابع چگونه تعریف میشود؟ برای پاسخ به این سوالات به مثال زیر توجه شود.
هر تابع به سایر توابع دسترسی دارد، اگر:
- در سطح بالای (بلافاصله) آن تابع قرار گیرد. (در کد زیر، تابع A می تواند با B یا D،تماس بگیرد اما با C یا E نه.)
- تابعی در همان سطح از تابع parent قرار گرفته باشد. (تابع B می تواند D را فراخوانی کند، D می تواند با B تماس بگیرد.)
- تابعی در هر سطح پایین تر از آن قرار گرفته باشد. (تابع C می تواند B یا D را فراخوانی کند، اما E را نه.)
function A(x, y) % Main function B(x,y) D(y) function B(x,y) % Nested in A C(x) D(y) function C(x) % Nested in B D(x) end end function D(x) % Nested in A E(x) function E(x) % Nested in D disp(x) end end end
از کلید واژه end برای نشان دادن پایان هر عملکرد در یک فایل استفاده میشود. اگر توابع تو در تو باشند و بیش از یک تابع محلی در m-file نوشته شود، حتما باید end به کار رود. در غیر این صورت، کلمه end اختیاری است.
خطاهای رایج
در این بخش به بررسی چند خطایی که بیشتر دیده میشود میپردازیم.
برای اجرای تابع معمولاً ابتدا نام تابع نوشته میشود و سپس در داخل پرانتز دادههای مورد نیاز نام برده میشود. در اینصورت کاربر نباید از گزینههای Run برای فراخوانی تابع استفاده نماید. در صورت انجام با خطای زیر مواجه خواهیم شد.
همانطور که در تصویر مشاهده میشود، با کمبود آرگومانهای ورودی مواجه شدهایم.
اما توابعی که نیاز به وردی ندارند را به راحتی میتوان فراخوانی کرد. به کد ساخت یک تاس همگن دقت کنید.
function Dice x=rand; for i=1:6 if x>=(i-1)/6 && x<(i)/6 i end end end
چون این تابع نیاز به ورودی ندارد پس از کلیک برروی گزینه Run برنامه به درستی عمل میکند. توجه شود که از گزینهی Run Section استفاده نشود.
در صورت انجام با خطای زیر مواجه میشویم.
بجز خطاهای نوشتاری و ساختاری، یکی از رایجترین مشکلات مربوط به عدم شناخته شدن تابع توسط نرمافزار است.
هرگاه با این خطا مواجه شدید ابتدا صحت نام وارده را بسنجید. درصورتی که از درستی نام تابع اطمینان حاصل کردید به سراغ Current Folder بروید. برای اجرای یک تابع حتماً باید Current Folder و پوشه ذخیرهسازی تابع یکی باشد.
بطور خلاصه در این مقاله چگونگی تعریف تابع در Script جداگانه، بیان شد. انواع آنها ،خواص هرکدام ،قوانین حاکم بر آنها و در نهایت خطاهای رایج هنگام کار با توابع بیان شد.
در ادامه با مثالهای کاربردی از توابع با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.