مقدمه‌ای بر برنامه‌نویسی تابع محور در پایتون
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 8 دقیقه

مقدمه‌ای بر برنامه‌نویسی تابع محور در پایتون

اخیراً، استفاده از برنامه‌نویسی تابع‌گرا افزایش یافته است؛ بنابراین بسیاری از زبان‌های برنامه‌نویسی مثل جاوا و پایتون، به پشتیبانی از تکنیک‌های برنامه‌نویسی تابع‌گرا روی آورده‌اند.

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

توابع First-Class

توابع، در پایتون first-class هستند؛ به این معنی که می‌توان آن‌ها را فقط به‌عنوان یک نوع داده دیگر مثل int درنظر گرفت.

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

توابع در قالب شی

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

ازآنجایی‌که توابع، شیء هستند، می‌توان تابع foo را به هر متغیری اختصاص داد و سپس آن متغیر را برای ارجاع دادن به تابع فراخوانی کرد. برای مثال، می‌توان آن را به متغیر bar به‌صورت زیر اختصاص داد:

عبارت bar = foo، شی که foo (نام تابع ما) به آن ارجاع می‌دهد را به متغیر bar اختصاص می‌دهد.

شیء در قالب توابع

شیء می‌تواند مثل توابع عمل کنند زمانی که آن‌ها را قابل فراخوانی تعریف می‌کنیم مثل: object(). این روش با استفاده از متد  __call__انجام می‌شود.

به مثال زیر توجه کنید:

هرزمان که یک شیء را از کلاس Greeter صدا می‌زنیم، شیء جدیدی می‌سازیم که می‌توان با یک نام جدید آن را فراخوانی کرد.

به مثال زیر توجه کنید:

چون ما متد __call__ را در تعریف کلاس داشتیم، توانستیم شیء morning را فراخوان کنیم. برای تشخیص اینکه یک شیء قابل فراخوانی هست یا نه از تابع callable همانند مثال زیر استفاده می‌کنیم:

توابع داخل ساختار داده

توابع، مانند سایر اشیاء می‌توانند داخل ساختار داده ذخیره شوند. برای مثال، ما می‌توانیم یک دیکشنری از int به func بسازیم. این، زمانی بکار می‌آید که int قسمت کوچکی از روش درحال اجرا باشد.

به‌صورت مشابه، توابع می‌توانند در سایر ساختارهای داده ذخیره شوند.

توابع در قالب آرگومان‌ها و مقادیر بازگشتی

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

توابع سطح بالا بسیار قدرتمند هستند و به‌خوبی در Eloquent JavaScript تعریف شده‌اند:

«توابع سطح بالا به ما کمک می‌کنند که نه‌تنها مقادیر بلکه عملیات را خلاصه کنیم.»

مثال زیر را در نظر بگیرید.

می‌خواهیم فهرستی از موارد را تکرار و سپس آن‌ها را چاپ کنیم. به‌راحتی می‌توانیم یک تابع iterate بسازیم:

این جالب به‌نظر می‌رسد، اما این تنها یک سطح از خلاصه‌سازی است. اگر بخواهیم کار متفاوتی (به‌جای چاپ کردن) بعد از تکرار لیست انجام دهیم، چطور کار را پیش می‌بریم؟

اینجاست که توابع سطح بالا مورداستفاده قرار می‌گیرند. می‌توانیم یک تابع iterate_custom ایجاد کنیم که هم فهرست را تکرار می‌کند و هم یک تابع به هرکدام از موارد اضافه می‌کند همانند مثال زیر:

اگرچه ممکن است چندان مهم به نظر نرسد، اما بسیار قدرتمند است.

ما کدمان را در یک سطح، خلاصه و مجدداً قابل‌استفاده کردیم. اکنون می‌توانیم تابع را نه‌تنها برای چاپ یک فهرست، بلکه برای انجام هرکاری که شامل تکرارهای متوالی است، فراخوانی کنیم.

 برای ایجاد چیزهای ساده‌تر هم می‌توان از توابع استفاده کرد. به همان روشی که توابع را در یک dict ذخیره می‌کنیم، می‌توانیم از یک تابع به‌عنوان یک جریان کنترل برای تصمیم‌گیری درمورد تابع مناسب استفاده کنیم.

به‌عنوان‌مثال:

توابع تودرتو

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

توابع کمکی زمانی کاربردی هستند که مسئله‌ای نیاز به تعریف یک تابع خاص داشته باشد (آرگومان یا دستور) اما حل مسئله بدون پیروی از مقررات آسان‌تر باشد. 

بیاید یه مثال خوب دراین زمینه بزنیم:

فرض می‌کنیم که شما می‌خواهید یک تابع فیبوناچی fib(n)  تعریف کنید که یک آرگومان n دارد و باید nامین عدد فیبوناچی را برگردانیم.

یک راه ممکن برای تعریف چنین تابعی استفاده از توابع کمکی است که دو عبارت قبلی دنباله فیبوناچی را دنبال می‌کند (چون هر عدد فیبوناچی، مجموع دو عدد قبلی آن است).

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

لامبدا (Lambda)

چگونه می‌توانیم یک تابع بدون اختصاص نام به آن بنویسیم؟ چگونه می‌توان یک تابع کوتاه یک‌خطی (مثل توابع foo یا mult در مثال‌های بالا) نوشت؟

می‌توان از lambda برای تعریف چنین توابعی در پایتون استفاده کرد. به‌عنوان‌مثال:

رفتار تابع mult دقیقاً شبیه تابعی است که قبلاً با استفاده از def تعریف کردیم.

توجه داشته باشید که توابع lambda باید یک‌خطی باشند و نباید شامل یک عبارت بازگشتی که توسط برنامه‌نویس نوشته شده است، باشند.

درواقع، آن‌ها همیشه یک عبارت ضمنی بازگشتی دارند (در مثال بالا، گفته می‌شود: return x * y؛ اما ما عبارات ضمنی بازگشتی را از تابع lambda حذف می‌کنیم).

تابع lambda خیلی قدرتمند و کوتاه است چون با استفاده از آن می‌توان توابع بدون نام ایجاد کرد:

این روش برای زمانی مفید است که فقط یک‌بار به یک تابع نیاز داریم و بعداً استفاده‌ای از آن نمی‌کنیم. به‌عنوان‌مثال، زمانی که یک دیکشنری را کامل می‎‌کنیم:

اکنون می‌توانیم به توابع Map، Filter و Reduce نگاه کنیم و بیشتر از قبل از روش lambda قدردانی کنیم.

تابع Map

Map تابعی است که مجموعه‌ای از ورودی‌ها را دریافت می‌کند و آن‌ها را بر اساس یک تابع مشخص، به مجموعه دیگری تبدیل می‌کند. عملکرد آن شبیه تابع iterate_custom است که قبلاً درمورد آن صحبت کردیم. 

به‌عنوان‌مثال:

در پایتون 3، تابع map یک شیء map را بازمی‌گرداند و می‌تواند تبدیل به یک لیست شود تا بتوانیم از آن استفاده کنیم. اکنون به‌جای تعریف تابع جداگانه multiply_by_four، می‌توانیم یک lambda تعریف کنیم:

می‌توانید مشاهده کنید که 4 خط کد بالا را در 1 خط خلاصه کردیم و این واقعاً عالی است.

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

تابع Filter

Filter همان‌طور که از اسمش پیداست، تابعی است که به فیلتر کردن مواردی که نیاز به آن‌ها نداریم، کمک می‌کند. به‌عنوان‌مثال، ممکن است بخواهیم تمام اعداد فرد را از scores حذف کنیم. می‌توان این کار را با استفاده از filter انجام داد:

ازآنجاکه نحوه عملکرد این تابع به این صورت است که مورد به مورد برای پذیرش هر آیتم تصمیم‌گیری می‌کند، باید مقدار bool را (که در تابع lambda مثال بالا هم دیده می‌شود)، برگرداند و یکنواخت باشد (یک ورودی پارامتری دریافت کند).

تابع Reduce

Reduce تابعی برای خلاصه‌سازی یا گرفتن یک تصویر بزرگ از مجموعه‌ای از داده‌هاست. به‌عنوان‌مثال، اگر بخواهیم جمع تمام نمرات را حساب کنیم، باید از reduce استفاده کنیم:

این بسیار ساده‌تر از نوشتن یک حلقه است. توجه داشته باشید که این تابع برای انجام عملیات نیاز به دو پارامتر دارد: یکی از آن‌ها نشان می‌دهد که آیتم کنونی تحت بررسی است و یکی دیگر نتایج حاصل از این عملیات را نشان می‌دهد.

کلام آخر

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

اهمیت برنامه‌نویسی تابعی در پایتون

برنامه‌نویسی تابعی دقیقا چیست؟

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

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@alireza.mzh
علیرضا معمارزاده
junior level developer

Student of Software Engineering, python Developer, i love programming and game

دیدگاه و پرسش

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

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید