پایتون و Functional Programming

functional programming

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

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

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

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

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

اعمال یک تابع روی لیستی از آیتم‌ها: map

با کمک تابع درونی map در پایتون می‌تونیم روی لیستی از آیتم‌ها، یک عملیات خاصی رو با کمک یه تابع دیگه انجام بدیم. خب یعنی چی؟ فرض کنید لیستی از اعداد در بازه‌ی ۱ تا ۱۰ دارید و قصد دارید همه‌ی این اعداد رو به توان ۲ برسونید. به راحتی با کمک map می‌تونید اینکار رو انجام بدین. در زیر یک مثال دیگه از عملکرد این تابع رو با هم می‌بینیم:

>>> map(lambda x: x + "bzz!", ["I think", "I'm good"])
<map object at 0x7fe7101abdd0>
>>> list(map(lambda x: x + "bzz!", ["I think", "I'm good"]))
['I thinkbzz!', "I'm goodbzz!"]

توجه کنید که خروجی map در پایتون ۲ یک لیست هست و در پایتون ۳ یک آبجکت از map به شما برگشت داده میشه (که می‌تونید اون رو به لیست تبدیل کنید). همونطور هم که می‌تونید ببینید در مثال بالا ما با استفاده از lambda یک تابع رو تعریف کردیم. شما می‌تونید بجای عبارت lambda و تعریف تابع در یک خط، از تابع دیگه‌ای که قبلا تعریف کردید استفاده کنید.

همچنین ما می‌تونیم معادل تابع map رو به صورت تعریف یک مولد (generator) یا لیست مثل زیر هم پیاده سازی کنیم:

>>> (x + "bzz!" for x in ["I think", "I'm good"])
<generator object <genexpr> at 0x7f9a0d697dc0>
>>> [x + "bzz!" for x in ["I think", "I'm good"]]
['I thinkbzz!', "I'm goodbzz!"]

فیلتر مقادیر یک لیست: filter

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

>>> filter(lambda x: x.startswith("I "), ["I think", "I'm good"])
<filter object at 0x7f9a0d636dd0>
>>> list(filter(lambda x: x.startswith("I "), ["I think", "I'm good"]))
['I think']

همچنین دو دستور زیر هم عملکردی مشابه با تابع filter دارن:

>>> (x for x in ["I think", "I'm good"] if x.startswith("I "))
<generator object <genexpr> at 0x7f9a0d697dc0>
>>> [x for x in ["I think", "I'm good"] if x.startswith("I ")]
['I think']

دریافت اندیس در هنگام کار با حلقه ها: enumerate

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

i = 0
while i < len(mylist):
    print("Item %d: %s" % (i, mylist[i]))
    i += 1

بجای استفاده از کد بالا، می‌تونید به راحتی از تابع enumerate در پایتون استفاده کنید تا حلقه‌های for شما به صورت پایتونیک نوشته بشن:

for i, item in enumerate(mylist):
    print("Item %d: %s" % (i, item))

مرتب سازی عناصر یک لیست: sorted

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

>>> sorted([("a", 2), ("c", 1), ("d", 4)])
[('a', 2), ('c', 1), ('d', 4)]
>>> sorted([("a", 2), ("c", 1), ("d", 4)], key=lambda x: x[1])
[('c', 1), ('a', 2), ('d', 4)]

در مثال بالا، لیستی از تاپل‌ها رو یکبار بر اساس آیتم اول تاپل و بار دیگه بر اساس آیتم دوم هر تاپل مرتب کردیم. همچنین می‌تونید با قرار دادن مقدار True برای آرگومان reverse در این تابع، عناصر لیست رو به صورت برعکس مرتب کنید.

بررسی شرط روی لیستی از عناصر: any و all

دو تابع any و all یک لیست دریافت به عنوان آرگومان ورودی دریافت می‌کنن و بر اساس مقادیر موجود در اون لیست، یک مقدار بول (boolean) برمی‌گردونن. کد این دو تابع تقریبا مثل قطعه کدهای زیر هست:

def all(iterable):
    for x in iterable:
        if not x:
            return False
    return True

def any(iterable):
    for x in iterable:
        if x:
            return True
    return False

کاربرد این دو تابع در مواقعی هست که بخوایم ببینیم که آیا تمام (all) یا یک کدوم (any) از مقادیر یک لیست شرط مورد نظر رو دارن یا نه. برای مثال در قطعه کد زیر یک لیست رو در دو حالت بررسی می‌کنیم:

mylist = [0, 1, 3, -1]
if all(map(lambda x: x > ۰, mylist)):
    print("All items are greater than 0")
if any(map(lambda x: x > ۰, mylist)):
    print("At least one item is greater than 0")

در مثال بالا، تابع any در صورتی مقدار True برمی‌گردونه که حداقل یکی از عناصر موجود در لیست در شرط مورد نظر صدق کنه. در صورتی که تابع all در صورتی مقدار True برمی‌گردونه که تمام عناصر موجود در لیست در شرط مورد نظر صدق کنه.

ترکیب چندین لیست با یکدیگر: zip

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

>>> keys = ["foobar", "barzz", "ba!"]
>>> map(len, keys)
<map object at 0x7fc1686100d0>
>>> zip(keys, map(len, keys))
<zip object at 0x7fc16860d440>
>>> list(zip(keys, map(len, keys)))
[('foobar', 6), ('barzz', 5), ('ba!', 3)]
>>> dict(zip(keys, map(len, keys)))
{'foobar': 6, 'barzz': 5, 'ba!': 3}

حرف پایانی

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

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

2 دیدگاه On پایتون و Functional Programming

جوابی بنویسید:

آدرس ایمیل شما به صورت عمومی منتشر نخواهد شد.