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

 توابع بازگشتی در جاوااسکریپت

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

فرض کنید یک تابع به نام

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function recurse() {
// ...
recurse();
// ...
}
function recurse() { // ... recurse(); // ... }
function recurse() {
    // ...
    recurse();
    // ...
}

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function recurse() {
if(condition) {
// stop calling itself
//...
} else {
recurse();
}
}
function recurse() { if(condition) { // stop calling itself //... } else { recurse(); } }
function recurse() {
    if(condition) {
        // stop calling itself
        //...
    } else {
        recurse();
    }
}

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

مثال‌هایی از تابع بازگشتی در جاوااسکریپت

در ادامه چند مثال از توابع بازگشتی را باهم بررسی می‌کنیم.

۱) یک مثال ساده از تابع بازگشتی در جاوااسکریپت

فرض کنید می‌خواهیم تابعی بنویسیم که از یک عدد مشخص شروع به شمارش معکوس کند تا به عدد ۱ برسد. برای مثال شمارش معکوس از ۳ به ۱:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
۳
۲
۱
۳ ۲ ۱
۳
۲
۱

قطعه کد زیر تابع

countDown()
countDown()را نشان می‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function countDown(fromNumber) {
console.log(fromNumber);
}
countDown(3);
function countDown(fromNumber) { console.log(fromNumber); } countDown(3);
function countDown(fromNumber) {
    console.log(fromNumber);
}

countDown(3);

countDown(3)
countDown(3)فقط عدد ۳ را نشان می‌دهد.

برای شمارش معکوس از عدد ۳ تا ۱، می‌توانیم به ترتیب زیر عمل کنیم:

به این ترتیب می‌توانیم تابع

countDown()
countDown()را به شکل بازگشتی بنویسیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function countDown(fromNumber) {
console.log(fromNumber);
countDown(fromNumber-1);
}
countDown(3);
function countDown(fromNumber) { console.log(fromNumber); countDown(fromNumber-1); } countDown(3);
function countDown(fromNumber) {
    console.log(fromNumber);
    countDown(fromNumber-1);
}

countDown(3);

تابع

countDown(3)
countDown(3)تا زمانی که از اندازه stack فراخوانی شده فراتر رود، اجرا خواهد شد. در نهایت با خطای زیر مواجه می‌شویم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Uncaught RangeError: Maximum call stack size exceeded.
Uncaught RangeError: Maximum call stack size exceeded.
Uncaught RangeError: Maximum call stack size exceeded.

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

شمارش معکوس زمانی متوقف می‌شود که عدد بعدی صفر شود. بنابراین برای حل این مشکل یک شرط if را به صورت زیر اضافه می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function countDown(fromNumber) {
console.log(fromNumber);
let nextNumber = fromNumber - 1;
if (nextNumber > 0) {
countDown(nextNumber);
}
}
countDown(3);
function countDown(fromNumber) { console.log(fromNumber); let nextNumber = fromNumber - 1; if (nextNumber > 0) { countDown(nextNumber); } } countDown(3);
function countDown(fromNumber) {
    console.log(fromNumber);

    let nextNumber = fromNumber - 1;

    if (nextNumber > 0) {
        countDown(nextNumber);
    }
}
countDown(3);

خروجی به شکل زیر خواهد بود:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
۳
۲
۱
۳ ۲ ۱
۳
۲
۱

تا این مرحله اینطور به‌نظر می‌رسد که تابع

countDown()
countDown()مطابق انتظاری که داشتیم کار می‌کند.

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let newYearCountDown = countDown;
// somewhere in the code
countDown = null;
// the following function call will cause an error
newYearCountDown(10);
let newYearCountDown = countDown; // somewhere in the code countDown = null; // the following function call will cause an error newYearCountDown(10);
let newYearCountDown = countDown;
// somewhere in the code
countDown = null;
// the following function call will cause an error
newYearCountDown(10);

خطا:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Uncaught TypeError: countDown is not a function
Uncaught TypeError: countDown is not a function
Uncaught TypeError: countDown is not a function

نحوه کار قطعه کد بالا:

این کد باعث ایجاد خطا می‌شود زیرا در بدنه تابع

countDown()
countDown()به نام تابع
countDown
countDownاشاره می‌شود که در زمان فراخوانی تابع روی مقدار null تنظیم شده بود.

برای حل این مشکل می‌توانیم از یک عبارت تابع نام‌گذاری شده به صورت زیر استفاده کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let countDown = function f(fromNumber) {
console.log(fromNumber);
let nextNumber = fromNumber - 1;
if (nextNumber > 0) {
f(nextNumber);
}
}
let newYearCountDown = countDown;
countDown = null;
newYearCountDown(10);
let countDown = function f(fromNumber) { console.log(fromNumber); let nextNumber = fromNumber - 1; if (nextNumber > 0) { f(nextNumber); } } let newYearCountDown = countDown; countDown = null; newYearCountDown(10);
let countDown = function f(fromNumber) {
    console.log(fromNumber);

    let nextNumber = fromNumber - 1;

    if (nextNumber > 0) {
        f(nextNumber);
    }
}

let newYearCountDown = countDown;
countDown = null;
newYearCountDown(10);

۲) مثال محاسبه مجموع n عدد طبیعی

فرض کنید می‌خواهیم مجموع اعداد طبیعی از ۱ تا n را با استفاده از تکنیک بازگشتی محاسبه کنیم. برای انجام این کار، باید تابع

sum()
sum()را به‌ صورت بازگشتی و به شکل زیر تعریف کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sum(n) = n + sum(n-1)
sum(n-1) = n - 1 + sum(n-2)
...
sum(1) = 1
sum(n) = n + sum(n-1) sum(n-1) = n - 1 + sum(n-2) ... sum(1) = 1
sum(n) = n + sum(n-1)
sum(n-1) = n - 1 + sum(n-2)
...
sum(1) = 1

تابع بازگشتی

sum()
sum() به صورت زیر است:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function sum(n) {
if (n <= 1) {
return n;
}
return n + sum(n - 1);
}
function sum(n) { if (n <= 1) { return n; } return n + sum(n - 1); }
function sum(n) {
  if (n <= 1) {
    return n;
  }
  return n + sum(n - 1);
}

جمع‌بندی