۲ - ۴تابع‌ها

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

درست مثل جبر لاندا، تمامی توابعِ هسکل یک آرگومان می‌گیرن و یک جواب برمی‌گردونن. وقتی به نظر میاد داریم چندتا آرگومان به یه تابع میدیم، در واقع داریم چندتا تابعِ تودرتو رو تک‌تک به یکی از آرگومان‌ها اعمال می‌کنیم. به این کار میگن currying.

ممکنه متوجه شده باشین که بیانیه‌هایی که تا الان استفاده کردیم فقط مقادیرِ معین داشتن و هیچ متغیر (یا تجرید) ی نداشتن. با توابع میشه اون بخشهایی از کد که می‌خوایم بعداً با مقادیرِ مختلف استفاده کنیم رو انتزاع کنیم.

برای مثال فرض کنید چندتا بیانیه رو میخواین با ۳ ضرب کنید. تک‌تک بیانیه‌ها رو اینطوری وارد می‌کنین:

Prelude> (1 + 2) * 3
9
Prelude> (4 + 5) * 3
27
Prelude> (10 + 5) * 3
45

این راهِ خیلی درستی نیست... با توابع میشه چنین الگوهای تکراری رو فاکتور کرد تا بعداً دوباره ازشون استفاده کنیم. این کار رو با دادنِ یه اسم به تابع و معرفیِ یه متغیرِ مستقل به عنوان پارامترِ تابع انجام میدیم. توابع می‌تونن در بدنه‌ی بقیه‌ی توابع ظاهر بشن، یا حتی به عنوان آرگومان به بقیه‌ی توابع (یا حتی خودشون) استفاده بشن، درست مثل هر مقدار یا value ِدیگه‌ای.

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

اون ‏‎* 3‎‏ بخشِ مشترک بیانیه‌هاست. قسمتی که تو هر کدوم متفاوته، بیانیه‌ی جمعِ قبل از اون ضربه، پس بجاش یه متغیر میذاریم و یه اسم هم برای تابع‌مون تعریف می‌کنیم. وقتی یه مقدار رو با متغیر جایگزین می‌کنیم، تابع حسابش می‌کنه، با ۳ ضربش می‌کنه، و نتیجه‌ش رو برمی‌گردونه. در بخش بعد، همه‌ی اینها رو تو هسکل پیاده می‌کنیم.

تعریف کردن توابع

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

*

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

بین تعریفِ توابع توی GHCi، با تعریف اونها توی یه فایل، یه کم فرق هست. برای تعریفِ مقادیر یا توابع در GHCi باید از ‏‎let‎‏ استفاده کنید*:

Prelude> let triple x = x * 3

همین تابع رو توی فایل اینطوری می‌نویسیم:

triple x = x * 3
*

از نسخه‌ی ۸٫۰٫۱ GHC به بعد، دیگه استفاده از ‏‎let‎‏ برای تعریف در GHCi لازم نیست. با این حال ما فعلاً در این کتاب ‏‎let‎‏ ها رو نوشتیم تا کدها با نسخه‌های قدیمی‌تر هم سازگاری داشته باشند. البته نوشتنِ این ‏‎let‎‏ های اضافه نباید باعث خطایی بشن.

حالا هر بخش‌ش رو جداگانه بررسی می‌کنیم:

  triple x   =   x * 3
-- [1]  [2] [3]  [ 4 ]

۱.

این اسمِ تابع‌ه؛ که در واقع تعریف ِیه تابع‌ست. دقت کنید که با حرف کوچیک شروع شده.

۲.

پارامتر تابع. این پارامترهای تابع معادلِ سَرِ لاندا هستن و متغیرهای داخلِ بدنه‌ی تابع رو قید می‌کنن.

۳.

علامت ‏‎=‎‏ برای تعریفِ مقادیر یا توابع در هسکل استفاده میشه، و برای بررسی تساوی بین دو مقدار نیست.

۴.

بدنه‌ی تابع. در صورتِ اعمالِ تابع به یه مقدار، این بیانیه (بدنه‌ی تابع) رو میشه محاسبه کرد. اگه ‏‎triple‎‏ به یه آرگومان اعمال بشه، ‏‎x‎‏ به اون مقدار مقیّد میشه. اینجا بیانیه‌ی ‏‎x * 3‎‏، بدنه‌ی تابع‌ه. پس در یه بیانیه‌ای مثلِ ‏‎triple 6‎‏، ‏‎x‎‏ به ۶ مقیّد میشه.

اندازه‌ی حروف مهمه!

اسم توابع با حروف کوچیک شروع میشن. گاهی سَبکِ شُتُری برای تفهیمِ بهترِ نقش یه تابع کار خوبیه. ولی حرفِ اول باید کوچک باشه.

متغیرها هم با حرف کوچیک شروع میشن. لزومی هم نداره که تک حرفی باشن.

بازی با تابعِ ‏‎triple‎‏

اول سعی کنین تابعِ ‏‎triple‎‏ رو با ‏‎let‎‏ تویِ REPL تعریف کنین. بعد تابع رو با یه مقدارِ عددی (به ازای آرگومان‌ش) صدا بزنین:

Prelude> triple 2
6

حالا بدونِ ‏‎let‎‏، توی یه فایل تعریف و ذخیره‌ش کنین. بعد که با دستور ‏‎:load‎‏ یا ‏‎:l‎‏ توی GHCi بارگذاری‌ش کردین، می‌تونین تابع رو با اسم‌ش، یعنی ‏‎triple‎‏، و یه مقدارِ عددیِ جلوش، صدا بزنین؛ همونطوری که بالاتر توی REPL انجام دادین. با مقادیر مختلف — اعداد صحیح یا حتی یه معادله‌ای مثل ‏‎(1 + 1)‎‏ — تست کنین. بعدش سعی کنین خود تابع رو توی فایل تغییر بدین. فایل رو دوباره بارگذاری کنید و نتیجه‌ی تغییرات رو ببینید. برای بارگذاری ِ دوباره‌ی همون فایل، می‌تونین از دستور ‏‎:reload‎‏ یا ‏‎:r‎‏ استفاده کنین.