دالة الاستدعاء الذاتي: العد التنازلي
جزء من قسم Logic & Flow في رحلة Dart على Coddy — الدرس 48 من 65.
حان الوقت الآن لوضع العودية (recursion) موضع التنفيذ باستخدام دالة عد تنازلي بسيطة. سيساعدك هذا التمرين على فهم كيفية عمل الحالة الأساسية (base case) والخطوة العودية (recursive step) معاً في كود حقيقي.
تبدأ دالة العد التنازلي برقم وتطبع كل رقم وصولاً إلى الصفر. إليك كيف تجعل العودية (recursion) هذا يعمل: إذا كان الرقم أكبر من الصفر، فاطبعه ثم استدعِ الدالة نفسها برقم أصغر. عندما يصل الرقم إلى الصفر، توقف.
void countdown(int number) {
if (number <= 0) {
print("Done!");
return; // الحالة الأساسية - توقف هنا
}
print(number);
countdown(number - 1); // الخطوة العودية
}تتحقق الحالة الأساسية (base case) مما إذا كان number <= 0 - وهذا هو الوقت الذي نتوقف فيه عن استدعاء الدالة. تقوم الخطوة العودية (recursive step) باستدعاء countdown(number - 1)، وهي نفس المشكلة ولكن بمدخلات أصغر، مما يقربنا تدريجياً نحو الحالة الأساسية.
عندما تستدعي countdown(3)، فإنه يطبع 3، ثم يستدعي countdown(2)، الذي يطبع 2، ثم يستدعي countdown(1)، الذي يطبع 1، ثم يستدعي countdown(0)، الذي يطبع "Done!" ويتوقف.
التحدي
سهلقم بإنشاء برنامج ينفذ مؤقت عد تنازلي تكراري (recursive) مع رسائل مخصصة. سيقوم برنامجك بتوضيح مفهوم التكرار (recursion) من خلال العد التنازلي من رقم معين وعرض رسائل مخصصة في كل خطوة.
- اقرأ مدخلاً نصياً يمثل رقم البداية للعد التنازلي
- اقرأ مدخلاً نصياً يمثل بادئة الرسالة المخصصة (مثلاً،
"Launch in"، أو"Timer"، أو"Countdown") - قم بتحويل المدخل الأول إلى عدد صحيح باستخدام
int.parse() - أنشئ دالة تكرارية تسمى
customCountdownتأخذ معاملين: - عدد صحيح
numberيمثل قيمة العد التنازلي الحالية - سلسلة نصية
messagePrefixلبادئة الرسالة المخصصة - يجب أن تنفذ الدالة المنطق التالي:
- الحالة الأساسية (Base case): إذا كان الرقم أقل من أو يساوي 0، اطبع
"Countdown complete!"ثم قم بالإنهاء (return) - الخطوة التكرارية (Recursive step): اطبع بادئة الرسالة متبوعة بالرقم الحالي، ثم استدعِ الدالة نفسها مع
number - 1 - استدعِ الدالة
customCountdownمع الرقم المحول وبادئة الرسالة - اعرض النتائج بالتنسيق الدقيق الموضح أدناه
على سبيل المثال، إذا كانت المدخلات هي "5" و "Launch in"، يجب أن يخرج برنامجك:
Starting countdown from: 5
Message prefix: Launch in
========================
Launch in 5
Launch in 4
Launch in 3
Launch in 2
Launch in 1
Countdown complete!إذا كانت المدخلات هي "3" و "Timer"، يجب أن يخرج برنامجك:
Starting countdown from: 3
Message prefix: Timer
========================
Timer 3
Timer 2
Timer 1
Countdown complete!إذا كانت المدخلات هي "0" و "Ready"، يجب أن يخرج برنامجك:
Starting countdown from: 0
Message prefix: Ready
========================
Countdown complete!يجب أن ينفذ برنامجك الدالة التكرارية customCountdown التي تستدعي نفسها برقم متناقص حتى تصل إلى الحالة الأساسية. يجب أن تطبع الدالة الرسالة المخصصة مع الرقم الحالي في كل استدعاء تكراري، مما يوضح كيف يقوم التكرار (recursion) بتفكيك المشكلة إلى مشكلات فرعية أصغر ومتطابقة. استخدم إدراج النصوص (string interpolation) لتنسيق رسائل العد التنازلي كـ "$messagePrefix $number".
ورقة مرجعية
توضح دالة العد التنازلي العودية كيفية عمل العودية من خلال العد التنازلي من رقم إلى الصفر:
void countdown(int number) {
if (number <= 0) {
print("Done!");
return; // الحالة الأساسية - توقف هنا
}
print(number);
countdown(number - 1); // الخطوة العودية
}تتحقق الحالة الأساسية مما إذا كان number <= 0 لإيقاف العودية. تستدعي الخطوة العودية نفس الدالة مع number - 1، للتحرك نحو الحالة الأساسية.
عند استدعاء countdown(3)، فإنها تطبع 3 ← 2 ← 1 ← "Done!" من خلال إجراء استدعاءات عودية متتالية بقيم متناقصة.
جرّب بنفسك
import 'dart:io';
// TODO: قم بإنشاء دالة customCountdown الخاصة بك هنا
// تذكر معالجة الحالة الأساسية (الرقم <= 0) والخطوة التكرارية
void main() {
// قراءة المدخلات
String? startingNumber = stdin.readLineSync();
String? messagePrefix = stdin.readLineSync();
// تحويل النص إلى عدد صحيح
int number = int.parse(startingNumber!);
// عرض المعلومات الأولية
print('Starting countdown from: $number');
print('Message prefix: $messagePrefix');
print('========================');
// TODO: استدعِ دالة customCountdown الخاصة بك هنا
// تذكر معالجة سلامة القيم الخالية (null safety) لـ messagePrefix
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Logic & Flow
1التعامل المتقدم مع القوائم
خصائص القائمة: first و lastحالة القائمة: isEmpty و isNotEmptyعكس القائمةالإضافة إلى القائمة: insertالحذف من القائمة: removeWhereالبحث في القائمة: indexOfفرز القائمةخلط القائمةملخص - منظم القوائم4معالجة الـ Map المتقدمة
التكرار عبر الـ Mapالتحقق من المفاتيح والقيمخصائص الـ Map: keys و valuesإضافة مشروطة: putIfAbsentإزالة العناصر من الـ Mapالـ Maps المتداخلةمراجعة - تحديث المخزون7الدوال المتقدمة
الدوال المجهولةتمرير الدوال كوسطاءفهم الـ Closuresمقدمة في الاستدعاء الذاتيدالة الاستدعاء الذاتي: العد التنازليدالة الاستدعاء الذاتي: العامليمراجعة - معالج القوائم2عمليات القوائم الوظيفية
التحويل باستخدام 'map'التصفية باستخدام 'where'استخدام '.toList()'التحقق من الشروط باستخدام 'any'الشروط باستخدام 'every'البحث باستخدام 'firstWhere'مراجعة - تصفية البيانات5مشروع: حاسبة عربة التسوق
إعداد المشروعإضافة العناصر إلى العربة3المجموعات
ما هي المجموعة؟إنشاء مجموعةالإضافة والحذف من المجموعاتالتحقق من وجود عناصر في المجموعةتحويل قائمة إلى مجموعةاتحاد المجموعاتتقاطع المجموعاتالفرق بين المجموعاتمراجعة - قائمة ضيوف فريدة