طلب البيتزا

طلب البيتزا

تمرين تعلّمي

مقدمة

العودية مفهوم قوي في البرمجة يقوم على استدعاء الدالة لنفسها. قد يكون فهمها صعبًا بعض الشيء في البداية، لكنك متى أدركت أساسياتها أصبحت أداة قيّمة لحل المسائل المعقدة. في هذا الدرس، سنستكشف العودية في JavaScript من خلال أمثلة سهلة الفهم.

ما هي العودية؟

تحدث العودية عندما تستدعي الدالة نفسها، سواء بشكل مباشر أو غير مباشر. وهي تشبه الحلقة، لكنها تقوم على تقسيم المسألة إلى مسائل فرعية أصغر وأسهل في التعامل معها.

مثال 1: العد التنازلي

لنبدأ بمثال بسيط: دالة العد التنازلي.

function countdown(num) {
  // Base case
  if (num <= 0) {
    console.log('Blastoff!');
    return;
  }

  // Recursive case
  console.log(num);
  countdown(num - 1);
}

// Call the function
countdown(5);

في هذا المثال:

  • الحالة الأساسية: عندما تصبح قيمة num أصغر من أو تساوي 0، تطبع الدالة "Blastoff!" وتتوقف عن استدعاء نفسها.
  • الحالة العودية: تطبع الدالة قيمة num الحالية وتستدعي نفسها بالقيمة num - 1.

مثال 2: المضروب

الآن، لنلقِ نظرة على مثال كلاسيكي على العودية: حساب مضروب عدد.

function factorial(n) {
  // Base case
  if (n === 0 || n === 1) {
    return 1;
  }

  // Recursive case
  return n * factorial(n - 1);
}

// Test the function
console.log(factorial(5)); // Output: 120

في هذا المثال:

  • الحالة الأساسية: عندما تكون قيمة n تساوي 0 أو 1، تُرجع الدالة 1.
  • الحالة العودية: تضرب الدالة n في مضروب n - 1.

المفاهيم الأساسية

الحالة الأساسية

يجب أن تحتوي كل دالة عودية على حالة أساسية واحدة على الأقل، وهي شرط تتوقف عنده الدالة عن استدعاء نفسها. وبدون حالة أساسية، ستستمر العودية بلا نهاية، مما يؤدي إلى تجاوز سعة المكدس.

الحالة العودية

تحدد الحالة العودية كيفية استدعاء الدالة لنفسها بنسخة أصغر أو أبسط من المسألة.

مزايا العودية وعيوبها

المزايا:

  • حل أنيق لبعض المسائل.
  • يحاكي مفهوم الاستقراء الرياضي.

العيوب:

  • قد يكون أقل كفاءة من الحلول التكرارية.
  • قد يؤدي إلى تجاوز سعة المكدس في حالات العودية العميقة.

الخلاصة

العودية تقنية قيّمة يمكنها تبسيط المسائل المعقدة عبر تقسيمها إلى مسائل فرعية أصغر وأسهل في التعامل معها. وفهم الحالات الأساسية والحالات العودية أمر بالغ الأهمية لتنفيذ حلول عودية فعّالة في JavaScript.

تعلّم المزيد:

التعليمات

أنت تدير متجر بيتزا، وتقدّم ثلاثة أنواع من البيتزا:

  • Margherita: $7
  • Caprese: $9
  • Formaggio: $10

إذا أراد العملاء، يمكنهم إضافة عدد غير محدود من الخيارات الإضافية: إما ExtraSauce مقابل $1 أو ExtraToppings مقابل $2.

مهمتك هي كتابة كود يساعد العميل في معرفة التكلفة التي سيدفعها.

حساب سعر البيتزا

عند تمرير اسم البيتزا كأول وسيط، مع عدد غير محدود من الخيارات المضافة، احسب سعر البيتزا بالدولارات.

pizzaPrice('Margherita');
// => 7

pizzaPrice('Caprese', 'ExtraSauce', 'ExtraToppings');
// => 12

pizzaPrice(
  'Caprese',
  'ExtraToppings',
  'ExtraToppings',
  'ExtraToppings',
  'ExtraToppings',
);
// => 17

حساب السعر الإجمالي للطلب

تُستدعى دالتك بقائمة من كائنات PizzaOrder، ويجب أن تُرجع السعر الإجمالي للطلب بالدولارات. كل PizzaOrder له خاصية pizza وهي اسم البيتزا، وخاصية extras وهي قائمة الخيارات الإضافية.

const margherita = new PizzaOrder('Margherita');
const caprese = new PizzaOrder('Caprese', 'ExtraToppings');
orderPrice([margherita, caprese]);
// => 18

ستدرك أنك لا تستطيع كتابة هذه الدالة باستخدام الاستدعاء الذاتي، لأن اختبارًا واحدًا بعدد هائل من الطلبات سيرفع الخطأ Maximum call stack size exceeded. لا تقلق، هذا مقصود. جرّب تنفيذ هذه الدالة باستخدام حلقة أمرية! لديك خيارات كثيرة، منها على سبيل المثال لا الحصر استخدام reduce أو حلقة for.

Advanced

عندما يشغّل مفسّر JavaScript كود JavaScript، فإنه يتتبّع الدوال التي دخلها (التي بدأ استدعاءها) في بنية بيانات تُسمى "المكدّس". وعندما تُرجع الدالة (تنتهي)، تُزال من المكدّس.

لكن حجم هذا المكدّس محدود. الخطأ الأكثر شيوعًا هو دالة تكرّر استدعاء نفسها ولا تنتهي أبدًا. يُوضع كل استدعاء على المكدّس، وقبل أن يُرجع، يُوضع استدعاء آخر على المكدّس.

function kaboom() {
  kaboom()
}

kaboom()
// => RangeError: Maximum call stack size exceeded

يُظهر تتبّع المكدّس لهذا الخطأ السطر نفسه مرارًا وتكرارًا، وهذا منطقي لأن الدالة تستدعي نفسها. ورغم أنه لا فائدة عملية حقيقية له في معظم الحالات، يمكنك معرفة إلى أي ارتفاع يمكن أن يصل ذلك المكدّس.

let calls = 0;
function kaboom() {
  calls +=1 ;
  kaboom()
}

kaboom()
// => RangeError: Maximum call stack size exceeded

console.log(calls)
// => a number, generally higher than 10.000

لا يوجد سوى حلّين عمليين لخطأ المكدّس الناتج عن دالة تكرّر استدعاء نفسها بشكل متزامن:

  • تأكّد من أن الدوال تُرجع قبل الوصول إلى حد المكدّس، عادةً بإضافة حالة أساسية أو إصلاحها.
  • أعد كتابة الدالة التكرارية على شكل حلقة أمرية، تنفّذ جسم الحلقة دون الحاجة إلى الدخول في دالة، وبالتالي دون زيادة المكدّس.
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
JavaScript Exercism

مستعد لبدء طلب البيتزا؟

سجّل في Exercism لتتعلّم وتتقن JavaScript عبر 37 مفهومًا159 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.