التحكّم في التدفّق والحلقات

تمارين عملية: FizzBuzz ومسائل الأعداد

15 دقيقة الدرس 10 من 14

تمارين عملية: FizzBuzz ومسائل الأعداد

لقد تعلّمت الآن جميع أدوات التحكم في التدفق والحلقات التي يوفّرها Java. يجمع هذا الدرس الأخير كل شيء معًا من خلال ثلاث مسائل كلاسيكية ستصادفها في المقابلات التقنية ومراجعات الكود الحقيقية: FizzBuzz، والكشف عن الأعداد الأولية، وحساب المضروب (Factorial). العمل على هذه المسائل يُدرّبك على عادة ترجمة المتطلّبات البشرية إلى Java نظيفة وصحيحة.

المسألة الأولى — FizzBuzz

المطلوب: اطبع الأرقام من 1 إلى 100. لمضاعفات 3 اطبع Fizz، ولمضاعفات 5 اطبع Buzz، ولمضاعفات 3 و5 معًا اطبع FizzBuzz.

النقطة الجوهرية هي ترتيب الشروط. يجب أن يأتي الشرط المشترك (% 3 == 0 && % 5 == 0) أوّلًا، وإلّا لن يُصل إليه أبدًا:

public class FizzBuzz { public static void main(String[] args) { for (int i = 1; i <= 100; i++) { if (i % 3 == 0 && i % 5 == 0) { System.out.println("FizzBuzz"); } else if (i % 3 == 0) { System.out.println("Fizz"); } else if (i % 5 == 0) { System.out.println("Buzz"); } else { System.out.println(i); } } } }
خطأ شائع — ترتيب خاطئ: إذا وضعت شرط % 3 == 0 قبل الشرط المشترك، فإن الرقم 15 سيطبع Fizz ولن يصل أبدًا إلى FizzBuzz. ضع دائمًا الشرط الأكثر تحديدًا أوّلًا.

بديل نظيف يبني نص الإخراج قبل طباعته، مما يُجنّب تكرار System.out.println:

for (int i = 1; i <= 100; i++) { String result = ""; if (i % 3 == 0) result += "Fizz"; if (i % 5 == 0) result += "Buzz"; if (result.isEmpty()) result = String.valueOf(i); System.out.println(result); }
لماذا أسلوب بناء النص يتوسّع بشكل أفضل: إذا أُضيفت لاحقًا قاعدة لمضاعفات 7 مثلًا ("Jazz")، فستضيف if واحدة فقط بدلًا من إعادة كتابة كل الفروع. كتابة كود سهل التوسعة علامة على الخبرة.

المسألة الثانية — الكشف عن الأعداد الأولية

العدد الأولي أكبر من 1 وليس له قواسم سوى 1 ونفسه. الأسلوب الساذج يتحقق من كل عدد من 2 حتى n - 1، لكننا نحتاج فقط للتحقق حتى Math.sqrt(n): إذا كان لـ n عامل أكبر من جذره التربيعي، فإن العامل الأصغر المقابل يكون قد وُجد بالفعل.

public class Primes { public static boolean isPrime(int n) { if (n < 2) return false; // 0 و1 ليسا أوليَّين if (n == 2) return true; // 2 هو العدد الزوجي الأولي الوحيد if (n % 2 == 0) return false; // استبعاد سريع للأعداد الزوجية الأخرى for (int i = 3; i * i <= n; i += 2) { // التحقق من القواسم الفردية فقط if (n % i == 0) return false; } return true; } public static void main(String[] args) { System.out.println("الأعداد الأولية حتى 50:"); for (int i = 2; i <= 50; i++) { if (isPrime(i)) { System.out.print(i + " "); } } // الناتج: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 } }
لماذا i * i <= n بدلًا من i <= Math.sqrt(n)؟ كلاهما صحيح. صيغة الضرب تتجنب استدعاء الجذر التربيعي بالفاصلة العائمة في كل تكرار، مما يجعل الحلقة أسرع قليلًا. للأعداد التي ستتعامل معها كمبتدئ الفرق غير ملحوظ، لكنها الطريقة المعتادة في Java.

المسألة الثالثة — المضروب (Factorial)

مضروب العدد الصحيح غير السالب n (يُكتب n!) هو حاصل ضرب كل الأعداد الصحيحة الموجبة من 1 إلى n. بالتعريف، 0! = 1.

النسخة التكرارية — بسيطة وفعّالة:

public class Factorial { public static long factorial(int n) { if (n < 0) throw new IllegalArgumentException("n must be non-negative"); long result = 1; for (int i = 2; i <= n; i++) { result *= i; } return result; } public static void main(String[] args) { for (int i = 0; i <= 10; i++) { System.out.println(i + "! = " + factorial(i)); } } }

لاحظ أن نوع القيمة المُعادة long وليس int. حتى 13! (6,227,020,800) يتجاوز حدود int ذي 32 بتًا، لذا استخدام long يعطيك نتائج آمنة حتى 20!.

النسخة التعاودية للمرجع: المضروب هو أيضًا مثال كلاسيكي للتعاود (Recursion). الحل التعاودي يستدعي نفسه: factorial(n) = n * factorial(n - 1). الصيغة التكرارية أعلاه مُفضَّلة في الإنتاج لأنها لا تخاطر بتجاوز المكدّس (stack overflow) للمدخلات الكبيرة وأسهل في الفهم.

الجمع بين المفاهيم

المسائل الثلاث تتبع النمط ذاته الذي مارسته طوال هذه الوحدة:

  1. اقرأ القاعدة بلغة عادية.
  2. اختر الحلقة المناسبة (for لنطاق معروف، while عندما يكون شرط الخروج ديناميكيًا).
  3. داخل الحلقة، اكتب شروط if/else if/else مرتّبة من الأكثر تحديدًا إلى الأقل.
  4. استخدم break أو return المبكّر لإيقاف التكرار فور الحصول على الإجابة — لا تكمل الحلقة دون داعٍ.
نصيحة للمقابلات: عندما تُطرح عليك مسألة شبيهة بـ FizzBuzz، فكّر بصوت عالٍ قبل الكتابة. المحاورون يهتمون بطريقة تفكيرك بقدر اهتمامهم بالإجابة الصحيحة من المحاولة الأولى.

الخلاصة

لقد أكملت الآن وحدة التحكم في التدفق والحلقات. علّمتك FizzBuzz الترتيب الصحيح للشروط وأنماط بناء النصوص. وضّح اكتشاف الأعداد الأولية كيف أن ملاحظة رياضية واحدة (حدّ الجذر التربيعي) تحوّل حلقة بطيئة إلى حلقة سريعة. أثبت المضروب أهمية اختيار النوع الصحيح للأعداد وقيمة الحلول التكرارية على التعاودية للتراكمات البسيطة. هذه المسائل الثلاث أساس متين لكل تحدٍّ خوارزمي قادم.