تابع در برنامه نویسی چیست
تابع یعنی چند خط کد را یک جا جمع کنید، رویش اسم بگذارید، و از آن به بعد به جای تکرار آن خط ها فقط اسمش را صدا بزنید. ارزش واقعی اش هم صرفه جویی در تایپ نیست؛ این است که هر تصمیمی که داخل آن گرفته اید، از آن به بعد فقط در یک جا زندگی می کند.
- درس ۷ از ۱۴
- مقدماتی
- رایگان، بدون ثبت نام
چهار تکه ای که با هم یک تابع می سازند
-
ورودی
همان چیزی که بین نسخه های تکراری فرق می کرد. اگر چیزی فرق نمی کند، پارامتر لازم نیست.
-
منطق
کاری که تابع می کند و اسمش باید آن را بگوید. اگر برای گفتنش مجبور شوید «و» بگویید، دو تابع دارید.
-
خروجی
چیزی که با return تحویل داده می شود. تابعی که فقط چاپ می کند، خروجی ندارد و نمی شود در محاسبه بعدی استفاده اش کرد.
-
استفاده دوباره
هر تصمیمی که داخل تابع گرفته اید، از این به بعد فقط در یک جا زندگی می کند و در یک جا هم عوض می شود.
تکه چهارم آن چیزی است که تابع را از چند خط کد معمولی جدا می کند. کدی که فقط یک بار و یک جا لازم است، لازم نیست تابع شود.
آخرین بررسی: فکت ها و نام ابزارهای این درس در همین تاریخ با منابعشان بازبینی شده اند.
تابع دقیقا چیست و چه فرقی با چند خط کد معمولی دارد؟
تابع چند خط کد است که یک اسم دارد. همین اسم داشتن، تفاوت را می سازد: کدی که اسم ندارد فقط جایی در فایل شماست، ولی کدی که اسم دارد می تواند از هر جای برنامه صدا زده شود و شما دیگر لازم نیست بدانید داخلش چه خبر است.
def display_name(user):
name = user["name"].strip()
if name == "":
return "guest"
return nameدو کار جدا اینجا اتفاق می افتد و تازه کارها معمولا قاطی شان می کنند. خط اول تعریف تابع است: می گوید چنین چیزی وجود دارد و کارش این است. اجرا نمی شود، فقط ثبت می شود. فراخوانی جای دیگری است، وقتی می نویسید display_name(user_a)، و تازه آن وقت این پنج خط اجرا می شوند.
پس یک تابع که تعریف شده ولی هیچ جا صدا زده نشده، هیچ کاری نمی کند و هیچ خطایی هم نمی دهد. این یکی از رایج ترین دلایل «کد من اجرا نمی شود» است، مخصوصا وقتی کسی کد را داخل تابع می گذارد و یادش می رود پایین فایل صدایش بزند.
و بهترین تعریفی که از تابع می شود داد این نیست که تکرار را کم می کند؛ این است که به یک فکر اسم می دهد. وقتی می نویسید display_name، خواننده کد شما دیگر لازم نیست چهار خط بخواند تا بفهمد چه خبر است. یک اسم خوب، چهار خط توضیح را حذف می کند و همین بیشترین چیزی است که یک تابع به شما می دهد.
پارامتر و آرگومان چه فرقی دارند؟
این دو کلمه یک چیز را از دو طرف نگاه می کنند و به همین دلیل هم قاطی می شوند. پارامتر اسمی است که موقع تعریف تابع می گذارید، یعنی همان user در خط اول نمونه بالا. آرگومان مقدار واقعی است که موقع صدا زدن می فرستید، یعنی همان user_a.
پارامتر یک جای خالی است و آرگومان چیزی که آن جای خالی را پر می کند. اگر بخواهیم دقیق تر بگوییم: پارامتر مال تعریف است و همیشه یکی است، آرگومان مال هر فراخوانی است و می تواند هر بار فرق کند. تابعی که یک پارامتر دارد ممکن است در طول برنامه پنجاه بار با پنجاه آرگومان مختلف صدا زده شود.
فایده دانستن این تفکیک عملی است و نه لغوی. وقتی خطایی می بینید که می گوید تابع دو ورودی می خواست و یکی گرفت، دارد درباره تعداد آرگومان ها حرف می زند، نه درباره تعریف تابع. یعنی تعریف سالم است و اشتباه در همان خطی است که صدایش زده اید. همین یک تشخیص، محل خطا را از کل فایل به یک خط کوچک می کند.
یک عادت که کارتان را ساده می کند: پارامترها را طوری نام گذاری کنید که خودشان بگویند چه انتظاری دارند. def send(to, subject, body) بدون هیچ توضیحی خوانده می شود، ولی def send(a, b, c) شما را مجبور می کند هر بار برگردید داخل تابع را بخوانید، که دقیقا همان چیزی است که تابع قرار بود از آن خلاصتان کند.
پارامتر، آرگومان و آنچه برمی گردد
تعریف: پارامتر
یک بار نوشته می شود
- یک جای خالی با یک اسم
- اسمش باید بگوید چه انتظاری دارد
فراخوانی: آرگومان
هر بار می تواند فرق کند
- مقدار واقعی که جای خالی را پر می کند
- یک تابع، پنجاه فراخوانی، پنجاه آرگومان
و یک چیز برمی گردد
مقدار بازگشتی
چیزی که با return تحویل داده می شود، نه چیزی که چاپ می شود
- بدون return، تابع چیزی خالی برمی گرداند
- return همان جا تابع را تمام می کند
یک تابع یک تعریف دارد و هر تعداد فراخوانی. خطایی که می گوید تعداد ورودی ها نمی خواند، همیشه درباره سمت راست این تصویر است نه سمت چپ.
return یعنی چه و چرا print جای آن را نمی گیرد؟
این نقطه ای است که بیشترین تعداد تازه کارها روی آن گیر می کنند، و ریشه اش یک بدفهمی ساده است: print چیزی را روی صفحه نشان می دهد، ولی return مقداری را به کسی که تابع را صدا زده تحویل می دهد. این دو اصلا یک کار نیستند.
def double(n):
print(n * 2)
result = double(5)
print(result)روی صفحه اول 10 چاپ می شود و بعد None. عدد ده را خود تابع چاپ کرده، ولی result خالی است، چون این تابع هیچ چیزی برنگردانده. تابعی که return ندارد، در پایتون None تحویل می دهد و در زبان های دیگر معادلش را.
def double(n):
return n * 2
result = double(5)
print(result)حالا هیچ چیزی داخل تابع چاپ نمی شود و result برابر ده است، یعنی چیزی که می توانید در محاسبه بعدی به کار ببرید. تفاوت عملی اش این است: تابعی که چاپ می کند وقتی دستی امتحانش می کنید درست به نظر می رسد، و همان لحظه ای می شکند که بخواهید از نتیجه اش استفاده کنید. برای همین هم این خطا معمولا دیر پیدا می شود.
یک نکته دیگر که کمتر گفته می شود: return بلافاصله تابع را تمام می کند. هر خطی که بعد از آن داخل همان تابع نوشته شده باشد اجرا نمی شود. این محدودیت نیست، ابزار است: در نمونه اول همین صفحه، return "guest" باعث می شود خط آخر اصلا سنجیده نشود. به این کار خروج زودهنگام می گویند و کد را از تو در توی شرط ها نجات می دهد.
یک بازسازی واقعی: سه بار تکرار، یک تابع
فرض کنید در سه جای برنامه همین چهار خط را نوشته اید، فقط با یک متغیر متفاوت:
name = user_a["name"].strip()
if name == "":
name = "guest"
print(name)
name = user_b["name"].strip()
if name == "":
name = "guest"
print(name)روش درست بیرون کشیدن تابع سه قدم دارد و قدم دومش همان قدمی است که معمولا رد می شود. اول تکرار را پیدا کنید. دوم، و مهم تر، پیدا کنید چه چیزی بین این سه نسخه فرق می کند؛ همان چیز، پارامتر شماست. سوم، یک تابع بنویسید که آن تفاوت را ورودی بگیرد.
def display_name(user):
name = user["name"].strip()
if name == "":
return "guest"
return name
print(display_name(user_a))
print(display_name(user_b))چیزی که واقعا به دست آمده صرفه جویی در چند خط نیست. این است که کلمه guest حالا فقط در یک جا نوشته شده. فردا که تصمیم بگیرید به جایش چیز دیگری بنویسید، یک خط عوض می کنید و کار تمام است.
و در نسخه قبلی چه اتفاقی می افتاد؟ دو تا از سه جا را عوض می کردید و سومی را نمی دیدید. برنامه هم خطایی نمی داد، فقط یک صفحه از سایت شما ماه ها کلمه قدیمی را نشان می داد و کسی متوجه نمی شد. تکرار کد به این دلیل خطرناک است، نه به این دلیل که زشت است.
پنج قدم بیرون کشیدن یک تابع از کد تکراری
-
۱
تکرار را پیدا کن
دو بار ممکن است اتفاقی باشد، سه بار دیگر نیست.
-
۲
تفاوت ها را پیدا کن
هر چیزی که بین نسخه ها فرق می کند، یک پارامتر است.
-
۳
اسم بگذار
اگر اسمی پیدا نکردید، هنوز نمی دانید این کد چه کار می کند.
-
۴
یک تابع بنویس
تفاوت ها ورودی می شوند و بقیه داخل تابع می ماند.
-
۵
هر سه جا را عوض کن
اگر یکی را جا بگذارید، همان یکی است که ماه ها رفتار قدیمی را نگه می دارد.
قدم دوم همان قدمی است که رد می شود و بعد بازسازی خراب می شود. اگر تفاوت بین نسخه ها را پیدا نکنید، تابعی می سازید که یکی از آن نسخه ها را بی سر و صدا عوض می کند.
چهار خط کد که ۱۸۱ بار صدا زده می شود
مثال بالا ساختگی است. این یکی نیست. کتابخانه اینفوگرافیک همین سایت یک تابع دارد که کارش فقط یک چیز است: از یک آرایه چهارزبانه، متن زبان صفحه را دربیاور، و اگر آن زبان نبود فارسی را بده.
function rgb_dg_t( $value, string $lang ): string {
if ( is_array( $value ) ) {
return (string) ( $value[ $lang ] ?? $value['fa'] ?? '' );
}
return (string) $value;
}روزی که این درس بررسی شد، این تابع در چهار فایل کتابخانه دیاگرام ۱۸۱ بار صدا زده شده بود. هر عنوان، هر برچسب، هر یادداشت زیر هر شکلی که در بخش آموزش می بینید، از همین چهار خط رد می شود.
حالا به آن ?? 'fa' وسط خط سوم نگاه کنید. آن یک تصمیم است: اگر ترجمه یک برچسب هنوز نوشته نشده، به جای یک شکل خالی، متن فارسی نشان داده شود. این تصمیم در یک جا نوشته شده و روی ۱۸۱ نقطه اثر می گذارد. اگر فردا نظرمان عوض شود و بخواهیم به جای فارسی چیزی نشان ندهیم، یک کلمه عوض می شود.
و روی دیگر همین سکه که باید صریح گفته شود: یک اشتباه در این چهار خط هم بلافاصله ۱۸۱ جا خراب می کند. تابعی که این قدر مرکزی است، دقیقا به همین دلیل باید از کدی که صدایش می زند با دقت بیشتری خوانده شود. تمرکز، هم سود می دهد و هم ریسک، و هر دو از یک جنس اند.
چه وقت تابع ننویسیم؟ و جایی که ما عمدا کد را تکرار کردیم
قاعده «هیچ چیز را تکرار نکن» را زیاد می شنوید و مثل هر قاعده مطلقی، جایی از کار می افتد. تابعی که برای فهمیدن چهار خط کد مجبورتان کند به بالای فایل بروید و برگردید، بیشتر از آن چیزی که صرفه جویی می کند از شما وقت می گیرد.
سه نشانه که یک تابع لازم نبوده: فقط یک جا صدا زده می شود و احتمالا هم فقط همان جا صدا زده خواهد شد؛ اسمش عملا همان کد داخلش است، مثل تابعی به اسم add_one که یکی اضافه می کند؛ و برای اینکه بشود نوشتش، شش پارامتر لازم دارد. آخری مهم ترین است: تعداد زیاد پارامتر معمولا یعنی این تابع بیش از یک کار می کند. آزمون ساده اش این است که سعی کنید در یک عبارت کوتاه بگویید چه کار می کند؛ اگر مجبور شدید بگویید «و»، دو تابع دارید.
حالا نمونه عکسش، از همین سایت. دو افزونه جدا داریم و هر دو باید قیمت را به تومان با جداکننده هزارگان و رقم فارسی نشان بدهند. کد این کار در هر دو تقریبا یکی است، ولی دو تابع جداست با دو اسم متفاوت، یکی در افزونه رتبه و یکی در افزونه هاست.
این تکرار اشتباه نیست، تصمیم است. هر افزونه باید مستقل کار کند، حتی اگر آن یکی خاموش باشد. اگر یکی از تابع آن یکی استفاده می کرد، غیرفعال کردن یک افزونه صفحه های افزونه دیگر را می شکست. هزینه ای که برای این استقلال می دهیم واقعی است و پنهانش نمی کنیم: هر تغییری در نحوه نمایش قیمت باید در دو جا انجام شود و باید یادمان بماند که دو جاست. این را برای اینکه بدانید همیشه راه سومی وجود ندارد می گوییم؛ گاهی باید بین دو هزینه یکی را انتخاب کنید و بعد آن انتخاب را جایی بنویسید تا نفر بعدی فکر نکند فراموش کرده اید.
مسیر سریع با هوش مصنوعی
بیرون کشیدن یک تابع از کد تکراری دقیقا همان کاری است که مدل ها خوب انجامش می دهند، و دقیقا همان کاری است که اگر بی دقت انجام شود بی سر و صدا رفتار برنامه را عوض می کند. پس دستور زیر یک بند غیرمعمول دارد که آن را از یک درخواست بازسازی ساده جدا می کند: از مدل می خواهد قبل از هر چیز فهرست کند سه نسخه در چه چیزهایی با هم فرق دارند. همان فهرست، چیزی است که خودتان ندیده اید و همان جایی است که بازسازی می شکند. کار قضاوت است، پس مدل قوی می خواهد و نه سریع؛ انتخاب فعلی ما میان مدل های کدنویسی، در بخش هوش مصنوعی همین سایت آمده است.
- هر سه نسخه تکراری را کامل کپی کنید، نه یکی از آنها با این توضیح که «بقیه هم شبیه همین اند». همان «شبیه» جایی است که بازسازی می شکند.
- دستور زیر را بدهید و اول فقط فهرست تفاوت ها را بخوانید، قبل از اینکه به کد پیشنهادی نگاه کنید. اگر در آن فهرست چیزی دیدید که خبر نداشتید، همان جا بایستید و اول تصمیم بگیرید کدام رفتار درست است.
- اسم پیشنهادی تابع را جدی بگیرید ولی کورکورانه قبولش نکنید. اگر برای توضیح دادن اسم مجبور شدید بگویید «و»، تابع دو کار می کند و باید دو تا شود.
- بعد از جایگزینی، هر سه جا را دوباره باز کنید و مطمئن شوید هیچ کدام جا نمانده. جا ماندن یکی از سه جا شایع ترین اشتباه این کار است و هیچ خطایی هم تولید نمی کند.
نسخه آماده کپی
این سه تکه کد در برنامه من تکرار شده اند و می خواهم یک تابع از آنها دربیاورم.
{تکه اول}
{تکه دوم}
{تکه سوم}
قبل از هر چیز و قبل از نوشتن هیچ کدی، این را بده:
۱. فهرست هر تفاوتی که بین این سه تکه هست، حتی تفاوت های ریز مثل ترتیب دو خط یا یک شرط اضافه در یکی از آنها.
۲. برای هر تفاوت بگو اگر همه شان را یک شکل کنیم، رفتار کدام تکه عوض می شود.
بعد از آن:
۳. یک تابع پیشنهاد بده که تفاوت های واقعی را پارامتر بگیرد، با اسمی که کارش را بگوید.
۴. سه فراخوانی که جای این سه تکه می نشینند.
۵. بگو تابع پیشنهادی روی ورودی خالی یا ناموجود چه رفتاری دارد.
اگر برای هیچ کدام از این ها به کدی نیاز داری که به تو نداده ام، بگو کدام و چرا؛ حدس نزن.
قبل از اعتماد به خروجی: بند اول این دستور ارزش کل دستور است و اگر برش دارید، به یک درخواست بازسازی معمولی تبدیل می شود که هر کسی می نویسد. و آخرین جمله هم بی دلیل نیامده: اگر یکی از آن سه تکه تابعی را صدا می زند که شما کدش را نفرستاده اید، مدل رفتارش را از روی اسمش حدس می زند و همان حدس را با لحن قطعی می نویسد. تا وقتی خودتان هر سه جا را بعد از جایگزینی اجرا نکرده اید، بازسازی تمام نشده است.
هوش مصنوعی در این کار
تابع نویسی از آن دسته کارهایی است که مدل های زبانی بی نقص انجام می دهند، چون شکلش ثابت است و نمونه اش در داده آموزش فراوان. پس کاری که برای شما می ماند قضاوت درباره قرارداد تابع است، نه نوشتنش: این تابع چه چیزی می گیرد، چه چیزی برمی گرداند، روی ورودی بد چه می کند، و آیا اسمش واقعا همان کاری را می گوید که انجام می دهد. موضع ما این است که برای این موضوع مدل را در نقش منتقد بگذارید و نه نویسنده: تابعی که خودش نوشته را از خودش نقد بخواهید، آن هم در یک پیام جدا.
ابزارهایی که واقعا کمک می کنند
- Claude Code برای این موضوع بهترین چیزی که دارد این است که مخزن را می بیند، پس وقتی می پرسید این تابع کجاها صدا زده شده، به جای حدس زدن می رود و می شمارد. همان کاری که ما در این درس روی تابع چهار خطی کردیم. خودش رایگان نصب می شود ولی بدون اشتراک کلود یا حساب کنسول انتروپیک کار نمی کند، و ایران در فهرست کشورهای پشتیبانی شده نیست.
- Claude برای نقد قرارداد یک تابع خوب جواب می دهد: بپرسید این تابع بیش از یک کار می کند یا نه، و اسمش دقیق است یا نه. ایران در هیچ یک از دو فهرست کشورهای پشتیبانی شده انتروپیک نیست؛ این را از صفحه خود انتروپیک خوانده ایم و نه از تست شبکه.
- Gemini حالت درس خواندنش به اسم Guided Learning به جای دادن جواب سوال می پرسد، و برای جا افتادن تفاوت return و print که سخت ترین بخش این درس است مناسب تر از جواب گرفتن مستقیم است. صفحه خود گوگل می گوید اپ وب جمنای در بیش از دویست و سی کشور و منطقه کار می کند و ایران در آن فهرست نیست.
کجا نتیجه معکوس می دهد
خطر مشخص این موضوع را می شود در یک جمله گفت: مدل بی درنگ تابعی می سازد که شش پارامتر دارد و سه کار انجام می دهد، چون شما گفتید «یک تابع» و او دقیقا یک تابع تحویل داده. کد کار می کند، تست هم می شود، و شش ماه بعد هیچ کس جرات نمی کند دستش بزند. پس تعداد پارامترها را خودتان بشمرید و هر بار بپرسید این تابع را می شود در یک عبارت کوتاه توصیف کرد یا نه.
خطر دوم دقیق تر است و مخصوص همین کار: وقتی از مدل می خواهید چند تکه مشابه را در یک تابع جمع کند، تفاوت های ریز بین آن تکه ها را عمدا یا سهوا صاف می کند، چون شما «یکی کردن» خواسته اید. نتیجه اش تغییر بی سر و صدای رفتار یکی از آن تکه هاست، بدون هیچ خطایی. انتروپیک خودش این دسته اطمینان بی پشتوانه را در مستنداتش توهم می نامد و روش های کم کردنش را توضیح می دهد. برای اینکه بدانید هر کدام از این ابزارها از ایران چطور قابل پرداخت است، راهنمای خرید را ببینید، و برای انتخاب فعلی ما میان مدل های کدنویسی، بهترین هوش مصنوعی برای کدنویسی.
منبع ها: Anthropic: reduce hallucinations Anthropic: supported countries Claude Code: overview and install Google: where the Gemini web app is available
حد این توصیه
نمونه های این صفحه پایتون اند و یکی از آنها PHP؛ ایده در همه زبان ها یکی است ولی نوشتنش فرق می کند. سه موضوع را هم عمدا نیاورده ایم، چون هر کدام درس خودشان را می خواهند: دامنه متغیرها و اینکه چه چیزی داخل تابع دیده می شود، مقدارهای پیش فرض و ورودی های اختیاری، و تفاوت تابع با متدی که روی یک شی صدا زده می شود. و یک سوال که جواب صادقانه اش «قاعده ای وجود ندارد» است: یک تابع چند خط باید باشد. هر عددی که به شما بدهند از جایی درنیامده؛ آزمون واقعی این است که بتوانید کارش را در یک عبارت کوتاه بگویید، و آن آزمون به تعداد خط ها ربطی ندارد.
از تجربه خود ما
عددی که در این درس آمده حدس نیست. روزی که این صفحه بررسی شد، در چهار فایل کتابخانه دیاگرام قالب همین سایت، تابع rgb_dg_t را شمردیم: ۲۱ بار در diagram.php، ۸۶ بار در diagram-lib.php، ۶۲ بار در diagram-lib2.php و ۱۲ بار در diagram-chart.php، جمعا ۱۸۱ فراخوانی برای تابعی که بدنه اش چهار خط است. چیزی که این عدد نشان می دهد و برای این درس مهم است، صرفه جویی در تایپ نیست: کل سیاست چندزبانه بودن اینفوگرافیک های این سایت در یک عبارت داخل همان تابع خلاصه شده، یعنی ?? $value['fa']، و همان یک عبارت تعیین می کند وقتی ترجمه یک برچسب نوشته نشده چه چیزی نمایش داده شود. اگر بخواهیم آن سیاست را عوض کنیم یک کلمه عوض می شود و ۱۸۱ نقطه با آن عوض می شوند؛ و اگر اشتباه کنیم، همان ۱۸۱ نقطه با هم خراب می شوند.
سوال هایی که واقعا پرسیده می شوند
فرق تابع و متد چیست؟
متد تابعی است که به یک شی چسبیده و معمولا روی داده همان شی کار می کند. در نمونه های این صفحه strip() یک متد است، چون روی خود رشته صدا زده می شود؛ display_name() یک تابع مستقل است. تا وقتی سراغ برنامه نویسی شی گرا نرفته اید، تفاوتشان فقط در شکل نوشتن است.
چرا تابع من None برمی گرداند؟
چون هیچ returnی در آن نیست، یا چون return در مسیری نوشته شده که در این اجرا از آن رد نشده اید. اولی معمولا یعنی به جای return از print استفاده کرده اید؛ دومی یعنی یک شاخه از شرط هایتان بدون return مانده. هر دو حالت را با نگاه کردن به آخرین خط تابع می شود تشخیص داد.
از کجا بفهمم یک تکه کد باید تابع شود؟
دو نشانه کافی است. اول اینکه سه بار تکرار شده باشد؛ دو بار ممکن است اتفاقی باشد، سه بار دیگر نیست. دوم اینکه بتوانید در یک عبارت کوتاه بگویید چه کار می کند؛ اگر آن عبارت را پیدا کردید، اسم تابع را هم پیدا کرده اید و اگر پیدا نکردید، هنوز نمی دانید آن کد چه کار می کند.