مستوى المقال: مبتدئ — مطلوب معرفة أساسية بـ Python أو JavaScript فقط
أكتر سؤال بيقع فيه المبتدئ أول ما يشوف دالة بتنادي نفسها هو: "هي بتعمل ايه دي بالظبط؟ ومش هتدخل في loop لا نهائي؟" الجواب القصير: لو الدالة فيها شرط توقف صح، لا. ولو مفيهاش، البرنامج هيقع في خطأ اسمه Stack Overflow بعد آلاف الاستدعاءات. Recursion مش حركة فلسفية ولا "كود ذكي"، هي تقنية حسابية ليها مكان محدد وقواعد ثابتة، ولما تستخدمها في المكان الغلط بتاكل ذاكرة من غير فايدة.
Recursion: ازاي الدالة تنادي نفسها بدون ما تقع في حلقة لا نهائية
المشكلة باختصار
لو عندك مشكلة طبيعتها متفرّعة — زي عرض شجرة فولدرات، تحليل JSON متداخل، أو حل لغز Sudoku — الحلقة العادية بـ for أو while مش هتنفع لأنك ما تعرفش العمق مقدمًا. Recursion بتحل المشكلة دي بفكرة بسيطة: الدالة بتعالج جزء صغير من المدخل، وبتنادي نفسها على الجزء الباقي. لما توصل لأبسط حالة، بتقف وتبدأ ترجع.
المشكلة إن المبتدئ بيستخدمها في مشاكل مش محتاجاها، فبيكسر الـ performance ويصعّب الكود على أي حد بعده. الهدف من المقال ده: تعرف امتى تستخدمها بثقة، وامتى تبعد عنها.
المثال الواضح: علب الماتريوشكا
تخيل ان عندك علبة ماتريوشكا روسية كبيرة. لما بتفتحها بتلاقي جواها علبة أصغر شبهها. تفتح الصغيرة دي بتلاقي علبة أصغر منها، وهكذا. كل علبة شبه اللي قبلها، بس أصغر. آخر علبة بتكون مصمتة — مفيش جواها حاجة، وانت هنا بتقف. ده بالظبط الـ Recursion.
الدالة بتقول لنفسها: "اعمل نفس الشغل، بس على مدخل أصغر". وفي مرحلة معينة، بتوصل لمدخل بسيط جدًا تعرف ترد عليه بدون ما تنادي نفسها تاني. الـ "أبسط حالة" دي اسمها Base Case، والاستدعاء على المدخل الأصغر اسمه Recursive Case. لو نسيت تحط Base Case، الدالة هتفضل تنادي نفسها لحد ما الذاكرة تخلص — بالظبط زي ما لو علب الماتريوشكا مفيش فيها علبة مصمتة في الآخر.
التعريف العلمي بالظبط
في كتاب CLRS (Cormen et al., Introduction to Algorithms)، الـ Recursion معرّفة كـ "دالة تستدعي نفسها على مدخل أصغر تقدم نحو حالة قاعدية تنهي السلسلة". فيه شرطين لا غنى عنهم:
- Base Case: حالة بسيطة بترد فيها الدالة قيمة مباشرة بدون استدعاء جديد.
- Recursive Case: استدعاء الدالة لنفسها بمدخل أقرب للـ Base Case.
لو فقدت أي شرط من الاتنين دول، الـ Recursion كارثة. بدون Base Case → loop لا نهائي → Stack Overflow. وبدون التقدم نحو الـ Base Case (يعني المدخل بيقل في كل استدعاء) → نفس الكارثة. القاعدة دي بتنطبق على أي لغة برمجة، مش Python بس.
أول كود: حساب factorial
أبسط مثال كلاسيكي هو حساب factorial (المضروب). تعريفه: n! = n × (n-1) × (n-2) × ... × 1. مثلاً 5! = 5 × 4 × 3 × 2 × 1 = 120. لاحظ ان 5! = 5 × 4!، وان 4! = 4 × 3!، وهكذا. ده تعريف ركركورسي بطبيعته: