آموزش کامل توابع بازگشتی در جاوا (به زبان ساده)

در این آموزش یاد می گیرید که تابع بازگشتی ایجاد کنید. تابعی که خودش را صدا می کند. همچنین با مزایا و معایب آن آشنا می شوید.
متدی که خود را فراخوانی می کند به عنوان متد بازگشتی شناخته می شود. این متد به عنوان تابع بازگشتی شناخته می شود.
یک مثال از دنیای فیزیکی ، قرار دادن دو آینه موازی در کنار یکدیگر است. هر چیزی که بین آن ها باشد به صورت بازگشتی منعکس می شود.
توابع بازگشتی (Recursive Functions) در جاوا یکی از مفاهیم زیبا و در عین حال چالشبرانگیز برنامهنویسی هستند. تابع بازگشتی، تابعی است که خودش را فراخوانی میکند. این تکنیک برای حل مسائلی که میتوانند به زیرمسئلههای کوچکتر تقسیم شوند (مثل پیمایش درختها یا محاسبات ریاضی خاص)، بسیار کارآمد است.
۱. ساختار اصلی یک تابع بازگشتی
هر تابع بازگشتی باید دو بخش اصلی داشته باشد تا دچار "حلقه بیپایان" (Stack Overflow) نشود:
-
شرط پایه (Base Case): شرطی که باعث توقف فراخوانیهای بعدی میشود.
-
گام بازگشتی (Recursive Step): بخشی که تابع در آن، خودش را با مقادیر کوچکتر فراخوانی میکند.
۲. مثال کلاسیک: محاسبه فاکتوریل
فاکتوریل عدد $n$ ($n!$) یعنی $n \times (n-1) \times ... \times 1$.
این تعریف را میتوان بازگشتی نوشت:
$n! = n \times (n-1)!$public class FactorialExample { public static void main(String[] args) { int number = 5; System.out.println("Factorial of " + number + " is " + factorial(number)); } public static long factorial(int n) { // شرط پایه: فاکتوریل ۰ یا ۱ برابر ۱ است if (n <= 1) { return 1; } // گام بازگشتی: n ضربدر فاکتوریل n-1 return n * factorial(n - 1); }}۳. نکات مهم برای مهندسین
-
Stack Overflow: هر بار که تابع خودش را صدا میزند، یک لایه جدید در حافظه (Stack) ایجاد میشود. اگر تعداد فراخوانیها بسیار زیاد باشد، حافظه پر شده و برنامه با خطای
StackOverflowErrorمتوقف میشود. بنابراین همیشه مطمئن شوید "شرط پایه" در نهایت محقق میشود. -
مقایسه با حلقه (Iteration): تقریباً هر مسئلهای که با بازگشت حل میشود، با حلقه (
forیاwhile) هم قابل حل است. حلقهها معمولاً بهینهتر هستند (حافظه کمتری مصرف میکنند)، اما توابع بازگشتی کد را تمیزتر و خواناتر میکنند (مخصوصاً در الگوریتمهای مرتبسازی). -
کاربرد در مهندسی:
-
ساختارهای درختی: پیمایش فایلها در سیستمعامل (هر پوشه میتواند پوشههای دیگری داشته باشد).
-
جستجوی دودویی (Binary Search): تقسیم کردن لیست به دو نیمه تا رسیدن به عنصر هدف.
-
الگوریتمهای تقسیم و غلبه (Divide and Conquer): مثل مرتبسازی سریع (QuickSort).
-
۴. نحوه عملکرد در حافظه (Visual Trace)
جاوا، جاوا اسکریپت رو قورت بده! بدون کلاس، سرعت 2 برابر، ماندگاری 3 برابر، پولسازی عالی با توسعه وب، ماشین لرنینگ و ... کتابخانه های پیشرفته جاوا اسکریپت و ... دانلود:
در مثال فاکتوریل ۵، اتفاقی که در حافظه میافتد به این صورت است: factorial(5) → 5 * factorial(4) → 4 * factorial(3) → 3 * factorial(2) → 2 * factorial(1) → 1 (شرط پایه) حالا مقادیر به صورت معکوس برمیگردند و در هم ضرب میشوند تا نتیجه نهایی (۱۲۰) حاصل شود.
۵. چه زمانی از بازگشت استفاده نکنیم؟
اگر مسئله شما ساده است و با یک حلقه معمولی قابل حل است (مثل چاپ اعداد ۱ تا ۱۰۰)، از بازگشت استفاده نکنید. بازگشت هزینهی حافظه (Memory Overhead) دارد و در حجم دادههای بسیار بالا، میتواند باعث کندی یا کرش کردن نرمافزار شود.
تابع بازگشتی چگونه کار می کند؟

برنامه بالا ، ابتدا تابع ()recurse از داخل تابع main (فراخوانی روش عادی) صدا زده می شود.
همچنین ، تابع ()recurse از داخل با همان تابع ()recurse فراخوانی می شود. این یک تابع بازگشتی است.
تابع بازگشتی به حالت عادی ادامه می یابد تا برخی از شرط ها برای جلوگیری از اجرای آن رخ دهد. اگر اینطور نباشد ، بازگشت بی نهایت رخ می دهد.
از این رو ، برای جلوگیری از ایجاد تابع بازگشتی نامحدود ، با دستور if … else (یا روش مشابه) در قسمتی از کد اتمام تابع بازگشتی را می نویسیم.
مثال: فاکتوریل یک عدد با استفاده از تابع بازگشتی
- class Factorial {
- static int factorial( int n ) {
- if (n != 0)
- return n * factorial(n-1); // recursive call
- else
- return 1;
- }
- public static void main(String[] args) {
- int number = 4, result;
- result = factorial(number);
- System.out.println(number + ” factorial = ” + result);
- }
- }
خروجی
۴ factorial = 24
در ابتدا ، ()factorial از تابع ()main با عدد ارسال شده به عنوان آرگومان فراخوانی می شود.
در داخل تابع ()factorial ، مقدار n در ابتدا ۴ است. در طی فراخوانی بازگشتی بعدی ، ۳ به تابع ()factorial ارسال می شود. این روند تا زمانی که n برابر با ۰ نباشد ادامه می یابد.
اگر n برابر با ۰ باشد ، شرط if اجرا نمی شود و قسمت else اجرا می شود که ۱ را بر می گرداند، و result به تابع ()main ارسال می شود.
شکل زیر ایده بهتری در مورد چگونگی عملکرد برنامه فوق ارائه می دهد.

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

