درس ۹ از ۴۹ · تابع‌ها

تابع‌ها

تابع‌ها تکه‌هایی از برنامه‌اند که نام دارند، ورودی می‌گیرند و نتیجه برمی‌گردانند. با آن‌ها برنامه را به بخش‌های کوچکی می‌شکنید که هر کدام یک کار را انجام می‌دهد، جداگانه خوانده می‌شود و بارها به کار می‌آید.

تعریف و فراخوانی

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

fn-basics.salam
تابع جمع ردیف(بها: صحیح، تعداد: صحیح): صحیح:
    برگشت بها * تعداد
پایان

تابع نمایش ردیف(نام: رشته، مبلغ: صحیح):
    سرچاپ نام + ":"، مبلغ
پایان

تابع ریشه:
    چای := جمع ردیف(۴۵، ۲)
    کیک := جمع ردیف(۱۲۰، ۱)
    نمایش ردیف("چای"، چای)
    نمایش ردیف("کیک"، کیک)
    نمایش ردیف("جمع کل"، چای + کیک)
پایان
خروجیچای: 90 کیک: 120 جمع کل: 210

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

برگشت زودهنگام

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

fn-early-return.salam
تابع بررسی نام کاربری(نام: رشته): رشته:
    اگر نام.شمارنویسه() < ۳:
        برگشت "خیلی کوتاه"
    پایان
    اگر نام.شمارنویسه() > ۱۲:
        برگشت "خیلی بلند"
    پایان
    اگر نام == "مدیر":
        برگشت "رزرو شده"
    پایان
    برگشت "درست"
پایان

تابع خوشامد(نام: رشته):
    اگر بررسی نام کاربری(نام) != "درست":
        سرچاپ "نمی‌توان خوشامد گفت به"، نام
        برگشت
    پایان
    سرچاپ "خوش آمدی،"، نام
پایان

تابع ریشه:
    سرچاپ بررسی نام کاربری("من")
    سرچاپ بررسی نام کاربری("مدیریت‌کل‌سامانه‌ها")
    سرچاپ بررسی نام کاربری("مدیر")
    خوشامد("مینا")
    خوشامد("آب")
پایان
خروجیخیلی کوتاه خیلی بلند رزرو شده خوش آمدی، مینا نمی‌توان خوشامد گفت به آب

این‌جا برای شمردن حرف‌ها از شمارنویسه() استفاده شده است، نه طول()؛ چون طول() بایت‌ها را می‌شمارد و هر حرف فارسی دو بایت است.

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

مقدار پیش‌فرض

ورودی‌ای که مقدار پیش‌فرض دارد در فراخوانی اختیاری است. این ورودی‌ها را بعد از ورودی‌های بدون پیش‌فرض بنویسید.

fn-params.salam
تابع خوشامد(نام: رشته، پیشوند: رشته = "سلام"):
    سرچاپ پیشوند + "،", نام
پایان

تابع مساحت(پهنا: صحیح، بلندی: صحیح = ۱): صحیح:
    برگشت پهنا * بلندی
پایان

تابع ریشه:
    خوشامد("مینا")
    خوشامد("مینا"، "درود")
    سرچاپ مساحت(۴)، مساحت(۴، ۵)
پایان
خروجیسلام، مینا درود، مینا 4 20

مقدار پیش‌فرض باید ثابت باشد یا از متغیرهای سراسری ساخته شود و نمی‌تواند به ورودی دیگری اشاره کند؛ مثلاً تابع جعبه(پ: صحیح، ب: صحیح = پ) خطای «شناسه‌ی ناشناخته» می‌دهد. به جایش یک تابع هم‌نام بنویسید (پایین‌تر آمده). سلام آرگومان نام‌دار ندارد و آرگومان‌ها همیشه به ترتیب ورودی‌ها نوشته می‌شوند.

گونه‌ی خروجی خودکار

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

fn-infer.salam
تابع میانگین(الف: اعشار۶۴، ب: اعشار۶۴):
    برگشت (الف + ب) / ۲.۰
پایان

تابع بیشینه(الف: صحیح، ب: صحیح):
    اگر الف > ب:
        برگشت الف
    پایان
    برگشت ب
پایان

تابع ریشه:
    سرچاپ میانگین(۳.۰، ۴.۰)، بیشینه(۸، ۳)
پایان
خروجی3.5 8

برگرداندن چند مقدار

تابع می‌تواند چند مقدار را به شکل یک چندتایی برگرداند. گونه‌ی برگشتی را به شکل (صحیح، صحیح) بنویسید، چندتایی را با برگشت الف، ب بسازید و هنگام فراخوانی با ساعت، دقیقه := تبدیل دقیقه(۱۳۵) آن را باز کنید. برای بخشی که لازم ندارید _ بنویسید و پیش از نامی که بعداً تغییر می‌کند ناپایا بگذارید. باید برای هر بخش دقیقاً یک نام بدهید، وگرنه کامپایلر با خطای ۱۵۸ می‌ایستد.

fn-tuples.salam
تابع تبدیل دقیقه(کل: صحیح): (صحیح، صحیح):
    برگشت کل / ۶۰، کل % ۶۰
پایان

تابع ریشه:
    ساعت، دقیقه := تبدیل دقیقه(۱۳۵)
    سرچاپ ساعت، "ساعت و"، دقیقه، "دقیقه"
    کامل، _ := تبدیل دقیقه(۲۰۰)
    سرچاپ کامل
    سفر := تبدیل دقیقه(۴۵)
    سرچاپ سفر.۰، سفر.۱
    سرچاپ سفر
پایان
خروجی2 ساعت و 15 دقیقه 3 0 45 (0, 45)

می‌توانید چندتایی را کامل نگه دارید و بخش‌هایش را با سفر.۰ و سفر.۱ بخوانید. چندتایی در هر جایی که گونه می‌آید کار می‌کند: پارامتر، فیلد ساختار یا وکتور<(صحیح، رشته)>. به شکل (0, 45) چاپ می‌شود و دو چندتایی را می‌شود با == مقایسه کرد.

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

fn-results.salam
ساختار زمان:
    همگانی ساعت: صحیح
    همگانی دقیقه: صحیح
پایان

تابع تبدیل دقیقه(کل: صحیح): زمان:
    برگشت زمان { ساعت = کل / ۶۰، دقیقه = کل % ۶۰ }
پایان

تابع ریشه:
    سفر := تبدیل دقیقه(۱۳۵)
    سرچاپ سفر.ساعت، "ساعت و"، سفر.دقیقه، "دقیقه"
    سرچاپ تبدیل دقیقه(۴۵)
پایان
خروجی2 ساعت و 15 دقیقه زمان {ساعت = 0, دقیقه = 45}

تابع بازگشتی

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

functions.salam
تابع فیبوناچی(ن: صحیح): صحیح:
    اگر ن < ۲:
        برگشت ن
    پایان
    برگشت فیبوناچی(ن - ۱) + فیبوناچی(ن - ۲)
پایان

تابع ریشه:
    تکرار ۱۰ در ک:
        چاپ فیبوناچی(ک), ""
    پایان
    سرچاپ ""
پایان
خروجی0 1 1 2 3 5 8 13 21 34

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

هم‌نامی

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

fn-overload.salam
تابع دوبرابر(عدد: صحیح): صحیح:
    برگشت عدد * ۲
پایان

تابع دوبرابر(متن: رشته): رشته:
    برگشت متن + متن
پایان

تابع دوبرابر(متن: رشته، جداکننده: رشته): رشته:
    برگشت متن + جداکننده + متن
پایان

تابع ریشه:
    سرچاپ دوبرابر(۲۱)
    سرچاپ دوبرابر("ها")
    سرچاپ دوبرابر("ها"، "-")
پایان
خروجی42 هاها ها-ها

نام‌های چندواژه‌ای

نام تابع می‌تواند چندواژه‌ای باشد تا فراخوانی مثل یک جمله خوانده شود. همه‌ی واژه‌ها بخشی از نام‌اند، پس هیچ‌کدام نباید کلیدواژه باشد: تابع چاپ خط به خاطر چاپ پذیرفته نمی‌شود.

fn-multiword.salam
تابع آخر هفته است(روز: صحیح): منطقی:
    برگشت روز == ۶ یا روز == ۷
پایان

تابع ریشه:
    سرچاپ آخر هفته است(۶)، آخر هفته است(۲)
پایان
خروجیtrue false

ورودی ارجاعی

ورودی‌ها به‌طور پیش‌فرض کپی می‌شوند و تغییرشان بیرون تابع دیده نمی‌شود. اگر پس از نام ورودی &: بنویسید، تابع روی خودِ متغیر فراخواننده کار می‌کند و می‌تواند تغییرش دهد. متغیری که می‌فرستید باید ناپایا باشد. ورودی ارجاعی جلوی کپی شدن یک ساختار بزرگ در هر فراخوانی را هم می‌گیرد.

fn-ref.salam
تابع افزایش(عدد &: صحیح):
    عدد++
پایان

تابع بی‌اثر(عدد: صحیح): صحیح:
    برگشت عدد + ۱
پایان

تابع ریشه:
    ناپایا امتیاز := ۱۰
    افزایش(امتیاز)
    افزایش(امتیاز)
    سرچاپ امتیاز, بی‌اثر(امتیاز), امتیاز
پایان
خروجی12 13 12

هر جا ممکن است، برگرداندن مقدار را ترجیح دهید: امتیاز = امتیاز بعدی(امتیاز) همان‌جا نشان می‌دهد چه چیزی عوض می‌شود. &: را برای تابعی نگه دارید که چیزی را در جا به‌روز می‌کند، مثل یک ساختار یا یک وکتور.

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

تابع را می‌شود به تابع دیگری هم داد. نام خالی یک تابع، بدون پرانتز، نشانی آن در حافظه است و گونه‌اش صحیح۶۴ است؛ همان چیزی که سامانه‌های فراخوان به سبک C می‌خواهند. برای فرستادن تابع به ورودی‌ای با گونه‌ی تابع، مثل تابع (صحیح) صحیح، آن را در یک تابع بی‌نام کوتاه بپیچید. درس بعد هر دو را توضیح می‌دهد.

ریشه و کد خروج

ریشه نقطه‌ی شروع برنامه است و آن‌چه برمی‌گرداند کد خروج برنامه است. رسیدن به پایانش، یا برگشت تنها، با کد ۰ یعنی «موفق» بیرون می‌رود. برگشت ۱ (یا هر عدد دیگری) به پوسته یا اسکریپتی که برنامه را اجرا کرده خبر می‌دهد که کار شکست خورده است. پیام‌های خطا را با نادرست‌سرچاپ بنویسید تا با خروجی عادی قاطی نشوند.

fn-main-exit.salam
تابع موجودی کافی(موجودی: صحیح، سفارش: صحیح): منطقی:
    اگر سفارش > موجودی:
        نادرست‌سرچاپ "موجودی کافی نیست:"، سفارش، "سفارش،"، موجودی، "مانده"
        برگشت نادرست
    پایان
    برگشت درست
پایان

تابع ریشه:
    دیرکن سرچاپ "دفتر سفارش بسته شد"
    اگر وارونه موجودی کافی(۱۲، ۵):
        برگشت ۱
    پایان
    سرچاپ "سفارش پذیرفته شد"
پایان
خروجیسفارش پذیرفته شد دفتر سفارش بسته شد

اگر ۲۰ عدد سفارش داده شود و فقط ۱۲ عدد موجود باشد، برنامه پیام را در جریان خطا می‌نویسد و با کد ۱ بیرون می‌رود؛ خط دیرکن‌شده در هر دو حالت اجرا می‌شود. صدا زدن سیستم عامل.خروج درون ریشه خطاست (E109)، چون دستورهای دیرکن ریشه را جا می‌اندازد، ولی برگشت آن‌ها را اجرا می‌کند. درس دیرکن این دستورها را توضیح می‌دهد.

چند قاعده‌ی سرانگشتی

  • هر تابع یک کار انجام دهد و نامش همان کار را بگوید.
  • حالت‌های نادرست را اول، هر کدام با یک برگشت زودهنگام، کنار بگذارید.
  • برای چند مقدار مرتبط یک چندتایی برگردانید، و وقتی بخش‌ها نام می‌خواهند یک ساختار.
  • مطمئن شوید همه‌ی مسیرهای تابعی که خروجی دارد به برگشت می‌رسند.
  • شکست را در ریشه با برگشت ۱ گزارش کنید، نه با سیستم عامل.خروج.