توابع بازگشتی

الگوریتم‌های بازگشتی

در ریاضیات کاربردی و به خصوص کامپیوتر، مسائل فراوانی وجود دارد که حل آن‌ها را به سادگی می‌توان به صورت یک الگوریتم بازگشتی نشان داد. یک الگوریتم بازگشتی مانند یک تابع یا یک دنباله بازگشتی تعریف می‌شود فرمان‌های الگوریتم به‌طور مکرر و با پارامترهای مختلف اجرا می‌شوند تا به فرمان بنیادی الگوریتم برسیم. آنگاه تمام مقادیری را که محاسبهٔ آن‌ها انجام نشده‌است را به صورت بازگشتی محاسبه می‌نماییم تا فرمان مورد نظر اجرا شود.
یک روش متداول برای آسان‌سازی مسائل این است که آن‌ها را به زیر مسائلی از همان نوع تقسیم بندی کنیم. این روش با نام گویشی کردن شناخته می‌شود. به عنوان یک تکنیک برنامه نویسی کامپیوتر، به این روش divide and conquer اطلاق می‌شود. و کلید راه حل تعداد زیادی از مسائل کامپیوتری مهم است و یک بخش اساسی می‌باشد.
تمام زبان‌های برنامه‌نویسی که امروز مورد استفاده‌اند تعریفی مستقیم از توابع بازگشتی در خود دارند. وقتی چنین تابعی فراخوانی می‌شود، کامپیوتر(برای اکثر زبان‌هایی که پشته دارند) یا خود کد زبان instanceهای مختلف تابع را (با فراخوانی پشته، هر چند روش‌های دیگر هم مورد استفاده قرار می‌گیرند). برعکس آن همهٔ توابع بازگشتی می‌توانند به کمک پشته به یک تابع غیر بازگشتی تبدیل شوند. اکثر توابع و روش‌هایی که می‌توانند بوسیلهٔ کامپیوتر ارزشیابی شوند بدون استفاده از غیر بازگشتی کردن قابل بازگشتی شدن هستند.

برنامه‌نویسی بازگشتی

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

ساختمان داده‌های بازگشتی(ساخت یافته)

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

لیست پیوندی

در زیر می‌توانید تعریف ساده‌ای از گرهٔ فهرستهای پیوندی را مشاهده کنید. عنصر بعدی در ساختار گره اشاره گری است به یک ساختار گره.
struct node{
  int n;              // some data
  struct node *next;  // pointer to another struct node};// LIST is simply a synonym for struct node * (aka syntactic sugar).typedef struct node *LIST;
رویه‌هایی که لیست ساختمان داده را به وجود می‌آورند می‌توانند به صورت بازگشتی هم تعریف شوند.
void printList(LIST lst){
    if (!isEmpty(lst))         // base case
    {
       printf("%d ", lst->n);  // print integer followed by a space
       printList(lst->next);   // recursive call
    }}

درخت‌های دودویی

در زیر می‌توانید تعریف ساده‌ای از درخت‌های دودویی را مشاهده کنید. همانند گره‌ها برای لیست‌های الحاقی در اینجا نیز تعریف به صورت بازگشتی است. در اینجا دو اشاره گر داریم، یکی به زیر درخت سمت چپ و دیگری به زیر درخت سمت راست.
struct node{
  int n;               // some data
  struct node *left;   // pointer to the left subtree
  struct node *right;  // point to the right subtree};// TREE is simply a synonym for struct node * (aka syntactic sugar).typedef struct node *TREE;
void printTree(TREE t) {
        if (!isEmpty(t)) {            // base case                          
                printTree(t->left);   // go left
                printf("%d ", t->n);  // print the integer followed by a space
                printTree(t->right);  // go right
        }}

بازگشتی در مقابل غیر بازگشتی

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

تابع1

ترتیب فراخوانی توابع تأثیر به سزایی در اجرای برنامه دارد. به مثال زیر که با زبان C نوشته‌است توجه کنید.
void recursiveFunction(int num) {
   if (num <5) {
      printf("%d\n", num);
      recursiveFunction(num + 1);
   }}

تابع2 معاوضه شده

void recursiveFunction(int num) {
   if (num <5) {
      recursiveFunction(num + 1);
      printf("%d\n", num);
   }}

بازگشت مستقیم و غیرمستقیم

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