- بواسطة x32x01 ||
الـRecursion (أو الدوال العودية) موضوع مهم جدًا في البرمجة وAlgorithms ✅
هنا هاشرحه بطريقة بسيطة وقريبة من اللي بنقراه ونكتبه في الكود، ومع أمثلة علشان تبقى الفكرة واضحة وتقدر تطبقها بسرعة 🚀
اللي بنعمله بالظبط:
= n * factorial(n-1) لحد ما نوصل لـ factorial(1).
شرح سريع:
هنا:
هنا هاشرحه بطريقة بسيطة وقريبة من اللي بنقراه ونكتبه في الكود، ومع أمثلة علشان تبقى الفكرة واضحة وتقدر تطبقها بسرعة 🚀
- ايه هي الفكرة الأساسية؟
- امتى أستخدمها؟
- إزاي أكتب دالة Recursive صحيحة وآمنة؟
- أمثلة عملية بكود (Python & JavaScript)
- نصايح وأخطاء شائعة لازم تتجنبها
الفكرة العامة: تعال نفهمه بمثال صندوق المفتاح🔑
تخيل معايا صندوق كبير جواه صناديق أصغر، وكل صندوق جواه صناديق كمان... وإحنا بندور على مفتاح داخل واحد من الصناديق.اللي بنعمله بالظبط:
- نفتح صندوق ونشوف هل المفتاح جوه؟
- لو لقيته، نوقف ونطلع المفتاح.
- لو مش لقيته، نطبق نفس الفكرة (نفس الخطوة) على كل الصناديق اللي جوه الصندوق ده.
- نكمل نكرر لحد ما نلاقيه أو ننتهى من كل الصناديق.
المكونات الأساسية لأي دالة Recursive 🧩
- Base case - الحالة اللي بنوقف فيها: لما نلاقي المفتاح أو نكون وصلنا لنهاية بدون نتيجة.
- Recursive case - الجزء اللي الدالة بتستدعى نفسها فيه على نسخة أبسط من المشكلة.
مثال بسيط بلغة Python 🐍
هنا مثال معروف: حساب العاملية (factorial). الفكرة إن factorial Python:
def factorial(n):
# Base case
if n <= 1:
return 1
# Recursive case
return n * factorial(n - 1)
print(factorial(5)) # الناتج 120 - لو n <= 1 - برجع 1 (ده الـBase case).
- لو n > 1 - بعمل نفس العملية على n-1 (ده الـRecursive case).
مثال عملي جوا الـ Algorithms: البحث في شجرة (Tree Search) 🌳
لو عندك شجرة أو مجلدات جوا بعضها وعايز تدور على ملف أو عنصر، Recursion بتسهل المهمة جدًا. المثال بتاع صندوق المفتاح هو نفس الفكرة. Python:
def find_key(box):
if box.has_key():
return box
for child in box.children:
found = find_key(child)
if found:
return found
return None هنا:
- الـBase case: لما الصندوق فيه المفتاح.
- الـRecursive case: بننادي find_key على كل الصناديق الفرعية.
متى تستخدم Recursion؟ ومتى لأ؟ ⚖️
- ممكن تستخدمها لما تكون المشكلة ليها بنية متكررة زي شجرات (trees)، قوائم متداخلة، تقسيم المشكلة لنصين (divide & conquer).
- متستخدمهاش لو كانت الStack depth هتبقى كبيرة جدًا - هنا استخدم حل Iterative أقل استهلاك للذاكرة.
- لو الأداء/الذاكرة مهمين جدًا - فكر في تحويلها لـ Iteration أو استخدام Tail Recursion (لو اللغة بتدعمها).
مثال JavaScript: بحث داخل شجرة عناصر DOM أو JSON 🧭
JavaScript:
function findNode(node, target) {
// Base case
if (node.value === target) return node;
// Recursive case
for (let child of node.children || []) {
const found = findNode(child, target);
if (found) return found;
}
return null;
} مشاكل شائعة ولازم تحذر منها ⚠️
- نسيان Base case → Infinite recursion → StackOverflow.
- استدعاءات كتير جدًا ممكن تستهلك ذاكرة: اتأكد إن العمق مش عالي.
- لو الدالة بتكرر حسابات لنفس القيم - استخدم Memoization (تخزين النتائج) لتسريعها.
تحسينات عملية: Memoization وTail Recursion 🛠️
- Memoization: مناسب للـDP والـFibonacci عشان متحسبش نفس القيمة كذا مرة.
- Tail Recursion: بعض اللغات بتدعم Tail Call Optimization وبتحول الRecursion لـLoop داخليًا عشان تحمي الStack.
Python:
# مثال سريع على Memoization (Python)
memo = {}
def fib(n):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib(n-1) + fib(n-2)
return memo[n] نصايح أخيرة علشان تبقى شاطر في استخدام Recursion 💡
- حدد الـBase case قبل ما تبدأ تكتب الكود.
- ارسم المشكلة (شجرة/مجلد) علشان تبص على التكرار.
- لو الأداء مهم، فكر في Memoization أو تحويلها لحل Iterative.
- اختبر الدالة لحالات صغيرة وكبيرة علشان تتأكد إنها مستقرة ومفيهاش تسريب Stack.
التعديل الأخير: