منطق در برنامه‌نویسی - تابع

May 22, 2019

این پست یک رشته توییت بوده که به پیشنهاد درست @ahangarha دارم اینجا میگذارمش تا راحت‌تر خونده بشه. پیشاپیش از غلط‌های نگارشی احتمالی عذرخواهی می‌کنم.

میدونید فرق تابع (function) با فرایند (procedure) و متد (method) و ماکرو (macro) چیه؟

تابع، به مفهوم ریاضیاتیش، صرفا محاسبه‌گر هست و مقدار خروجیش فقط به ورودیش بستگی داره. توابع اعمال جانبی ندارن و حق ندارن ورودی‌هاشون رو تغییر بدن.

مثلا اگر بهتون بگم x=2 y=3 و ازتون بخوام حاصل ضربشون رو بدید انتظار دارم نتیجه ۶ باشه (چیزی جز ورودی نباید روی نتیجه اثر بگذاره) و هیچ تغییری تو ماشین نباید رخ بده (اعمال جانبی) و اگه بعدش بپرسم حالا ایکس چنده تنها جواب معقول ۲ هست.

نمونه واقعی‌ترش رو بخوابم نگاه کنیم، دستور append یا push، دو آرگومان میگیرن، یک لیست و یک عنصر و اون عنصر رو به انتهای لیست اضافه می‌کنن. آیا این دستور تابع هست؟

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

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

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

list.push(x)

و چون پوش متدی از لیست هست اجازه داره تغییرات رو روش اعمال کنه.

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

push (makelist(), getx())

سیستم اول دستورات make list و get x رو اجرا میکنه بعد به عنوان ورودی میده به دستور push، اما وقتی پوش رو از نوع یک ماکرو تعریف کنیم ماهیت سیمبلیکشون کپی میشه در مقصد، مثلا ممکنه تبدیل به دستور زیر بشه:

insertAt(makelist(), 0, getx())

و بعد از اون سیستم مقادیر make list و get x رو حساب کنه. ماکروها رو توی زبان C با دستور define و در لیسپ با همین اسم دیدیم.

اما خب چرا اینها مهمه؟ اگر زبان برنامه‌نویسیتون بین این موارد تفاوت قائل نشه چرا باید برای شما مهم باشن؟

توی خیلی از زبان‌ها این تفاوت وجود داره، مثلا تو هسکل شما تابع دارید و موناد یا توی c++ از لحاظ پیاده سازی متدها با پوینتر ساخته میشن و ماکروها در پیش‌پردازش اعمال میشن.

تمایز قائل شدن از طرف برنامه‌نویس ولی میتونه فراتر از این باشه. هم توی موازی سازی و سرعت بخشیدن کمک کنه و هم دیباگ کردن و تغییر دادن سیستم رو راحت‌تر کنه.