🧩 فصل ۱۵: برنامهنویسی تابعی در پایتون(Functional Programming)

چرا برنامهنویسی تابعی (Functional Programming)؟
برنامهنویسی تابعی یکی از سبکهای برنامهنویسی است که تمرکز اصلیاش روی تعریف و استفاده از توابع خالص (توابعی که هیچ تأثیر جانبی ندارند) و کار با دادهها به شکل غیر قابل تغییر (immutable) است.
در پایتون، میتوانیم از قابلیتهای برنامهنویسی تابعی استفاده کنیم تا کد تمیزتر، کوتاهتر و قابل تست بنویسیم؛ حتی اگر کل پروژه طبق این سبک نباشد.
۱۵.۱ مقدمهای بر برنامهنویسی تابعی (FP)
برنامهنویسی تابعی (Functional Programming) روشی است که در آن سعی میکنیم با توابع خالص، بدون استفاده از وضعیت یا تغییر دادهها (immutable)، مسائل را حل کنیم. در این مدل:
- توابع فقط بر اساس ورودی، خروجی تولید میکنند.
- حالت پنهان، تغییر متغیر سراسری، و اثر جانبی به حداقل میرسد.
- دادهها را تغییر نمیدهیم، بلکه کپی جدید از آنها میسازیم.
تابع (Function) چی هست؟
توابع بخشهای جدایی از کد هستند که با ورودی مشخص کار انجام میدهند و خروجی میدهند.
مثال ساده:
def add(x, y):
return x + y
print(add(3, 4)) # خروجی: 7
تفاوت با روش معمول (imperative):
در روش دستوری (که بیشتر بهش عادت داریم)، گامبهگام وضعیت را تغییر میدهیم:
# روش معمول
nums = [1, 2, 3]
squared = []
for n in nums:
squared.append(n * n)
print(squared)
در Functional، این دستور را به شکل کوتاهتر و تمیزتری مینویسیم:
# روش تابعگرا
nums = [1, 2, 3]
squared = list(map(lambda n: n * n, nums))
print(squared)
۱۵.۲ توابع لامبدا (Lambda) چیست و چه تفاوتی با تابع معمولی دارد؟
معمولی:
def add(x, y):
return x + y
لامبدا:
add = lambda x, y: x + y
لامبداها سریع و فقط برای یک کار کوتاه مناسباند. مثلاً وقتی یک تابع فقط برای map یا filter میخواهی استفاده کنی.
۱۵.۳ توابع مهم: map، filter، reduce
map(func, iterable):
هر مقدار از iterable (مثل لیست) را وارد func میکند و خروجیها را به صورت یک شیء قابل پیمایش میدهد.
مثال: همه اعداد لیست را دو برابر کن
nums = [1, 2, 3, 4]
def double(x): # تابع معمولی هم میشه داد
return x * 2
doubled = list(map(double, nums))
print(doubled) # خروجی: [2, 4, 6, 8]
یا همون کار با lambda:
nums = [1, 2, 3, 4]
doubled = list(map(lambda x: x * 2, nums))
print(doubled)
filter(func, iterable):
همه اعضا را از نظر func بررسی میکند و فقط اعضای True را نگه میدارد.
مثال: فقط عددهای زوج را نگه دار
nums = [1, 2, 3, 4, 5, 6]
even = list(filter(lambda x: x % 2 == 0, nums))
print(even) # خروجی: [2, 4, 6]
reduce(func, iterable)
کاهش دادن لیست به یک مقدار. باید از functools ایمپورتش کنی.
مثال: ضرب همه اعضا
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product) # خروجی: 24 (یعنی 1*2*3*4)
مثال: جمع اعضا (مجموع)
total = reduce(lambda x, y: x + y, nums)
print(total) # خروجی: 10
۱۵.۴ ترکیب توابع map و filter
اغلب از این توابع پشت سرهم یا ترکیبی استفاده میشود تا نتایج بخواهیم بدست آوریم:
مثال کاربردی: اعداد لیست را دو برابر کن و فقط اعدادی که از ۵ بزرگترند نگهدار
nums = [1, 2, 3, 4, 5, 6]
double = map(lambda x: x * 2, nums)
filtered = filter(lambda x: x > 5, double)
print(list(filtered)) # خروجی: [6, 8, 10, 12]
۱۵.۵ ساختار تابعی: تابع در تابع، Higher-Order Functions
توابعی که تابع دیگر را به عنوان آرگومان میپذیرند یا برمیگردانند،
برای مثال:
def apply_func(func, value):
return func(value)
result = apply_func(lambda x: x ** 3, 4)
print(result) # خروجی: 64
توابع تو در تو و کلوزر (closure):
تابع داخلی به متغیرهای تابع بیرونی دسترسی دارد:
def power(n):
def inner(x):
return x ** n
return inner
cube = power(3)
print(cube(2)) # خروجی: 8
اینجا cube در واقع تابع جدیدی شد که هر عددی بهش بدهی، توان سومش را میدهد.
۱۵.۶ تابع خالص (Pure) و اثر جانبی (Side Effect)
تابع خالص: فقط به ورودیاش وابسته است و چیزی را خارج از خودش تغییر نمیدهد یا وابسته به هیچ دادهای نیست.
تابع غیر خالص (با اثر جانبی):
count = 0
def increase():
global count
count += 1 # وابسته به متغیر خارجی
increase()
تابع خالص:
def increase(n):
return n + 1
۱۵.۷ مفهوم immutable (تغییر ناپذیر بودن دادهها)
- لیست و دیکشنری، mutable هستند (میشود مقدارشان را تغییر داد).
- رشته، tuple و عدد، immutable هستند.
برنامهنویسی تابعی ترجیح میدهد روی دادههای immutable کار کند تا نتیجهی توابع هیچ تغییری ایجاد نکند.
🟩 تمرین عملی (پروژه کوچک با توضیح)
مسأله:
یک لیست از اعداد صحیح [1 تا 15] داشته باش.
۱. با map همه را دو برابر کن.
۲. با filter، مقادیر زوج را پیدا کن.
۳. با reduce، مجموع مقادیر بدست آمده را حساب کن.
کد:
from functools import reduce
nums = list(range(1, 16))
doubled = list(map(lambda x: x * 2, nums))
even = list(filter(lambda x: x % 2 == 0, doubled))
total = reduce(lambda x, y: x + y, even)
print("لیست اولیه:", nums)
print("دو برابر شده:", doubled)
print("زوجها:", even)
print("مجموع:", total)
توضیح:
۱. ابتدا همه دو برابر شدند.
۲. سپس فقط زوجها انتخاب شدند.
۳. در آخر مجموعشان را محاسبه کردیم.
نکته پایانی (برای حرفهایتر شدن):
- همه جای پروژه لازم نیست Functional باشی، اما گاهی برای پردازش لیستها یا دیتا، سریعتر و تمیزتر هست.
- اگر Lambda برات پیچیده بود، اول فانکشن معمولی تعریف کن، وقتی تسلط پیدا کردی برو سراغ Lambda.
- توابع map، filter و reduce توی پروژههای دادهکاوی، کوچینگ داده و حتی سادهترین پروژههای اتوماسیون به شدت استفاده میشوند.