حلقات for ودالة range — التكرار لعدد محدد من المرات
عندما تعرف عدد مرات التكرار، فإن حلقة for هي الخيار الصحيح. الأشكال الثلاثة لدالة range، ولماذا يُستثنى الحد الأخير، وكيف تختار بين for و while.
كيف تريد أن تتعلم؟
المشكلة التي نقوم بحلها
في الفصل السابق، تطلّب حساب مجموع الأرقام من 1 إلى 5 أربعة أسطر، سطران منها كانا مخصصين فقط لتتبع العدّ — قيمة ابتدائية ومقدار زيادة. وإذا نسيت مقدار الزيادة، فستقع في حلقة لانهائية.
لكن لاحظ أمراً مهماً حول هذا النوع من المهام: كنا نعرف مسبقاً عدد المرات التي سيتكرر فيها التنفيذ. خمس مرات. وعندما تكون هذه هي الحالة، لا يوجد سبب لإدارة العداد بنفسك — يمكنك ترك هذه المهمة لبايثون.
هذا هو بالضبط ما تفعله حلقة for. ولأن العدّ لم يعد بيدك، فإن حلقة for التي تمر على range لا تصبح لانهائية من تلقاء نفسها — يتحدد عدد المرات منذ بداية الحلقة. وهذه أكبر ميزة لها. (هناك استثناء: إذا كانت الحلقة تمر على قائمة (list) وأنت تضيف عناصر جديدة إلى القائمة نفسها داخل الحلقة، فلن تنتهي الحلقة أبداً. القوائم تأتي في الفصل التالي.)
في نهاية هذا الفصل ستكون قادراً على
- تشغيل الكود لعدد معروف من المرات باستخدام حلقة
for - استخدام الأشكال الثلاثة لدالة
range() - شرح لماذا ينتج عن
range(1, 5)أربعة أرقام وليس خمسة - التكرار على أحرف سلسلة نصية (string) حرفاً بحرف
- تحديد ما إذا كانت المهمة تتطلب استخدام
forأمwhile
المتطلبات السابقة: حلقات while — التكرار طالما تحقق الشرط.
أبسط صورة لحلقة for
for number in range(5):
print(number)0
1
2
3
4عمل ثلاثة أسطر في سطر واحد، ودون أي عداد تديره بنفسك. اقرأ هذا السطر على النحو التالي: "لكل رقم من الأرقام التي تنتجها دالة `range(5)`، نفّذ الكتلة البرمجية مرة واحدة — وفي كل مرة، ضع ذلك الرقم في المتغير `number`."
يظهر هنا أمران بوضوح:
يبدأ العدّ من الصفر، والعدد 5 لا يظهر أبداً — هناك خمسة أرقام إجمالاً، لكنها تبدأ من 0 حتى 4. هذه هي نفس القاعدة التي تطبقها بايثون في كل مكان: تماماً كما يُستثنى فهرس (index) النهاية في الشرائح، يُستثنى أيضاً في range.
اسم المتغير number هو اختيارك أنت. يمكنك كتابة for i in range(5) بنفس الطريقة، ويُعد i الاسم المعتاد للعدادات البرمجية. ولكن عندما يكون للرقم معنى محدد، اختر اسماً يعبر عن هذا المعنى — فكتابة for student_number in range(30) تشرح نفسها بنفسها.
الأشكال الثلاثة لدالة range()
print("--- range(5) ---")
for i in range(5):
print(i, end=" ")
print()
print("--- range(2, 6) ---")
for i in range(2, 6):
print(i, end=" ")
print()
print("--- range(0, 10, 3) ---")
for i in range(0, 10, 3):
print(i, end=" ")
print()
print("--- range(5, 0, -1) ---")
for i in range(5, 0, -1):
print(i, end=" ")
print()--- range(5) ---
0 1 2 3 4
--- range(2, 6) ---
2 3 4 5
--- range(0, 10, 3) ---
0 3 6 9
--- range(5, 0, -1) ---
5 4 3 2 1القواعد:
range(stop)— يبدأ من0، وينتهي قبلstoprange(start, stop)— يبدأ منstart، وينتهي قبلstoprange(start, stop, step)— يتقدم بمقدار الخطوةstepفي كل مرة؛ والخطوة السالبة تعني العد تنازلياً إلى الخلف
تأمل المثالين الأخيرين بعناية. الكود range(0, 10, 3) يعطي 0 3 6 9 — الرقم التالي سيكون 12 وهو أكبر من 10، لذلك يتوقف. والكود range(5, 0, -1) يعطي 5 4 3 2 1 — الرقم 0 لا يظهر أبداً لأن النهاية مستثناة هنا أيضاً.
لماذا يُستثنى الحد الأخير؟ لأن ذلك يجعل الحساب سهلاً. كم عدداً يوجد فيrange(0, 5)؟ الناتج هو5 - 0، أي خمسة أعداد. وفيrange(3, 8)؟ الناتج هو8 - 3، أي خمسة أعداد. اطرح فتحصل على العدد، دون أن تتساءل هل يجب إضافة واحد أم لا. وإذا وضعت نطاقين متجاورين — مثلrange(0, 5)وrange(5, 10)— فلن تفوت أي رقم ولن تكرر أي رقم.
عندما تريد الأرقام من واحد إلى عشرة
يتكرر هذا الخطأ كثيراً لدرجة أنه يستحق قسماً خاصاً به:
print("Wrong — stops at 9:")
for i in range(1, 10):
print(i, end=" ")
print()
print("Right — includes 10:")
for i in range(1, 11):
print(i, end=" ")
print()Wrong — stops at 9:
1 2 3 4 5 6 7 8 9
Right — includes 10:
1 2 3 4 5 6 7 8 9 10القاعدة في سطر واحد: اكتب كقيمة توقف (stop) رقماً أكبر بواحد من آخر رقم تريده.
تجميع القيم — الآن بالطريقة الأسهل
total = 0
for number in range(1, 6):
total = total + number
print("Sum of 1 to 5:", total)Sum of 1 to 5: 15قارن هذا بنسخة while لنفس المهمة: لا يوجد عداد تنشئه، ولا عداد تزيده يدوياً، ولا خطر الوقوع في حلقة لانهائية. الشيء الوحيد الذي يجب الانتباه له هو أن المجمّع (accumulator) لا يزال بحاجة إلى أن يبدأ خارج الحلقة، تماماً كما في السابق.
التكرار على سلسلة نصية
لا تمر حلقة for على الأرقام فقط — بل تمر أيضاً على السلسلة النصية حرفاً بحرف:
word = "Python"
for letter in word:
print(letter, end="-")
print()
vowel_count = 0
for letter in "programming":
if letter in "aeiou":
vowel_count = vowel_count + 1
print("Vowels:", vowel_count)P-y-t-h-o-n-
Vowels: 3لا توجد دالة range هنا لعدم الحاجة إليها — فالعبارة for letter in word تمر مباشرة على كل حرف. هذا أبسط من حساب الفهارس يدوياً، ولا يعرّضك لخطر الوقوع في خطأ IndexError.
وعندما تحتاج إلى الفهرس والحرف معاً، توجد الدالة enumerate()، وستأتي في فصل القوائم.
break و continue يعملان هنا أيضاً
يعمل الأمران تماماً كما كانا يعملان في حلقة while:
for number in range(1, 20):
if number % 7 == 0:
print("First multiple of 7:", number)
breakFirst multiple of 7: 7الاختيار بين for و while
اطرح على نفسك سؤالاً واحداً: هل عدد التكرارات معروف قبل أن تبدأ الحلقة؟
إذا كانت الإجابة نعم، فاستخدم for:
- التكرار خمس مرات
- الانتقال من 1 إلى 100
- فحص كل حرف في كلمة
- القيام بعمل ما مع كل عنصر في قائمة
إذا كانت الإجابة لا، فاستخدم while:
- الاستمرار في السؤال حتى تصبح المدخلات صحيحة
- التكرار حتى يكتب المستخدم كلمة
quit - القراءة طالما بقيت أسطر في الملف
عند الشك، اختر for. والسبب بسيط: حلقة for التي تمر على range أو على سلسلة نصية ثابتة مضمونة الانتهاء، بينما حلقة while ليست كذلك. وحيثما يعمل كلاهما، فالخيار الذي يسمح بأخطاء أقل هو الأفضل.
مثال متكامل
multiplication.py:
# Print a multiplication table, then a small summary
number = int(input("Which table? "))
upto = int(input("Up to? "))
print()
print(f"--- Table of {number} ---")
total = 0
for i in range(1, upto + 1):
result = number * i
total = total + result
print(f"{number} x {i:2} = {result:4}")
print()
print(f"Rows : {upto}")
print(f"Sum : {total}")
print(f"Average : {total / upto:.2f}")Which table? 7
Up to? 5
--- Table of 7 ---
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
Rows : 5
Sum : 105
Average : 21.00ثلاثة أشياء تستحق الانتباه. أولاً، range(1, upto + 1) — الوصول إلى upto استلزم كتابة رقم أكبر بواحد. ثانياً، {i:2} و {result:4} — داخل نصوص f-string يمكنك تحديد عرض المسافة التي يجب أن يشغلها الرقم، مما يحافظ على استقامة الأعمدة. ثالثاً، يبدأ total خارج الحلقة وينمو بداخلها — وهو نفس نمط المجمّع الذي رأيناه سابقاً.
واسأل نفسك سؤالاً واحداً: ماذا يحدث لو أجاب المستخدم بـ 0 عند السؤال عن Up to? سينتج عن range(1, 1) نطاق فارغ تماماً بدون أي أرقام، وبالتالي لن تنفذ الحلقة إطلاقاً — وعندها ستتسبب العملية total / upto في خطأ القسمة على صفر. وهذا هو الدرس نفسه من الفصل السابق: قبل استخدام ما جمعته الحلقة، تأكد من أنها جمعت شيئاً بالفعل.
حالات الخطأ الشائعة وحلولها
الرقم الأخير مفقود ينتهي range(1, 10) عند الرقم 9. أضف واحداً إلى حد التوقف: range(1, 11).
TypeError: 'float' object cannot be interpreted as an integer لا تقبل range() إلا أعداداً صحيحة (int). كتابة range(2.5) أو range(1, n / 2) لن تعمل — حوّل القيمة باستخدام int()، أو استخدم القسمة الصحيحة //.
SyntaxError: expected ':' نسيان النقطتين الرأسيتين في نهاية سطر for.
IndentationError: expected an indented block after 'for' statement السطر داخل الحلقة لم يُزح بمسافة بادئة.
الحلقة لم تنفّذ إطلاقاً نطاق range فارغ. ينتج عن range(5, 0) نطاق لا يحتوي على شيء — للعد تنازلياً تحتاج إلى الوسيط الثالث: range(5, 0, -1).
المجموع يساوي 0 بعد انتهاء الحلقة سطر total = 0 وُضع بالخطأ داخل الحلقة، مما يؤدي لإعادة ضبطه في كل دورة. يجب أن يكون خارج الحلقة.
متغير من داخل الحلقة مطلوب خارجها، ولكنه يسبب NameError كان نطاق range فارغاً، فلم تنفّذ الحلقة أبداً ولم يُنشأ المتغير أصلاً. امنحه قيمة ابتدائية قبل الحلقة.
الخطوة ٤ من ٦ — التوقع
تحقّق من فهمك
ماذا تطبع هذه الحلقة؟
for i in range(1, 5):
print(i, end=" ")- A1 2 3 4 5
- B1 2 3
- C0 1 2 3 4
- D1 2 3 4
الوسيط الثالث يحدد الخطوة. ما الناتج؟
for i in range(0, 10, 3):
print(i, end=" ")- A3 6 9
- B0 3 6 9
- C0 1 2 3 4 5 6 7 8 9
- D0 3 6 9 12
كان المقصود مجموع الأرقام من 1 إلى 10، أي 55. لكن البرنامج يطبع 45. لماذا؟
total = 0
for number in range(1, 10):
total = total + number
print("Sum of 1 to 10:", total)- Aالمتغير `total` بدأ خارج الحلقة، وكان يجب أن يبدأ داخلها
- Bاستخدام حلقة `while` بدل `for` سيعطي الجواب الصحيح
- Cيجب كتابة `total + i` بدل `total + number`
- D`range(1, 10)` ينتهي عند `9` ويستبعد `10` — يجب كتابة `range(1, 11)`
الإجابة تتطلب حساباً
سجّل الدخول للتحقق من إجاباتك
الأسئلة موجودة أعلاه، والتفكير في إجاباتها ذهنياً هو الجزء المهم. سجّل الدخول لرؤية الإجابات والتفسيرات والتلميحات ذات المستويات الثلاثة.
دورك الآن
اكتب ملفاً باسم stars.py يستقبل رقماً ويطبع مثلثاً من النجوم. إذا كان المدخل 5:
*
**
***
****
*****ثم أضف هذه المهام الثلاث:
- اطبع إجمالي عدد النجوم في الأسفل (15 للمدخل 5)
- اطبع المثلث مقلوباً أيضاً — خمس نجوم في الأعلى ونجمة واحدة في الأسفل
- اطبع نسخة تتخطى الصفوف ذات الأرقام الزوجية (استخدم
continue)
توجد طريقة سهلة للتحقق من المطلب الأول — احسب مجموع الأرقام من 1 إلى 5 يدوياً وانظر هل يتطابق برنامجك معه. وهل تتذكر "*" * i؟ إنشاء صف من النجوم لا يحتاج إلى حلقة داخل الحلقة.
الخطوة ٦ من ٦
التحدي — اختبار الفصل
عشرة أسئلة متدرجة من السهل إلى الصعب. الأسئلة الأخيرة صعبة عن قصد.
سجّل الدخول لأداء الاختبار