مقدمة إلى الحلقات المتداخلة (Nested Loops)
رأيت من قبل أن الجسم المزاح داخل if أو داخل حلقة يمكن أن يحتوي على أي نوع من التعليمات، بما في ذلك شروط if أخرى أو حلقات أخرى. وبالتحديد، يمكن أن توجد حلقة داخل حلقة.
إليك مثالًا:
for letter in 'ABC':
print(letter)
for number in range(4):
print(f'{letter} {number}')
print('---')
هذا يسمى حلقة متداخلة (nested loop). لا توجد هنا قاعدة جديدة تمامًا؛ نحن فقط نركّب الأدوات التي تعرفها بالفعل. لكن من المهم أن تفهم هذا التركيب جيدًا لأنه مفيد جدًا في كتابة برامج أكثر تعقيدًا.
تأكد أولًا أنك تفهم ما يحدث. السطران print(letter) وprint('---') يعمل كل منهما 3 مرات، لأن مستوى الإزاحة الخاص بهما يضعهما داخل الحلقة الخارجية. أما print(f'{letter} {number}') فيعمل 3 × 4 = 12 مرة، لأنه داخل الحلقة الداخلية for number in range(4): التي تنفذ 4 دورات في كل دورة من دورات الحلقة الخارجية الثلاث.
لنستخدم الفكرة عمليًا. تخيل أنك مدرس وتريد طباعة جداول الضرب من 1 إلى 12 للطلاب. بدل كتابتها يدويًا، اكتب برنامجًا ينتجها كلها. يجب أن يكون الناتج بهذا الشكل، مع أسطر الشرطات بين الجداول:
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
1 x 4 = 4
1 x 5 = 5
1 x 6 = 6
1 x 7 = 7
1 x 8 = 8
1 x 9 = 9
1 x 10 = 10
1 x 11 = 11
1 x 12 = 12
---
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
2 x 5 = 10
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
2 x 10 = 20
2 x 11 = 22
2 x 12 = 24
---
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
(ويستمر النمط نفسه...)
11 x 10 = 110
11 x 11 = 121
11 x 12 = 132
---
12 x 1 = 12
12 x 2 = 24
12 x 3 = 36
12 x 4 = 48
12 x 5 = 60
12 x 6 = 72
12 x 7 = 84
12 x 8 = 96
12 x 9 = 108
12 x 10 = 120
12 x 11 = 132
12 x 12 = 144
---
تحتاج إلى حلقة
forداخل حلقةfor.تحتاج إلى الأعداد من 1 إلى 12.
كلما احتجت تسلسلًا من الأعداد المتتابعة، استخدم
range.تريد شيئًا مثل
for x in range(n):.سيبدأ هذا بـ
x = 0، لكن حل ذلك سهل.أضف 1 إلى
xببساطة.استخدم
*لضرب الأعداد.استخدم f-string يحتوي على عدة متغيرات.
تذكر طباعة سطر بعدد الشرطات الصحيح بعد كل قسم.
تأكد أن كل سطر داخل الحلقة الصحيحة وبالمسافة البادئة الصحيحة.
ممتاز!
التمرين التالي: أنت تنظم بطولة للعبة مثل الشطرنج أو التنس، ولديك قائمة بأسماء اللاعبين:
players = ["Alice", "Bob", "Charlie"]
سيلعب كل لاعب ضد كل لاعب آخر مرتين: مرة تكون له فيها أفضلية البداية مثل الحركة أو الإرسال أولًا، ومرة لا تكون له هذه الأفضلية. اطبع جميع المواجهات بهذا الشكل:
Alice vs Bob
Alice vs Charlie
Bob vs Alice
Bob vs Charlie
Charlie vs Alice
Charlie vs Bob
لاحظ أن Alice vs Bob وBob vs Alice موجودتان معًا، لكن لا توجد Alice vs Alice لأننا لا نريد أن يلعب أحد ضد نفسه.
فكر كيف ستنفذ هذا يدويًا وبنظام باستخدام ورقة وقلم.
تحتاج إلى حلقة
forداخل حلقةfor.تحتاج إلى جملة
ifللتأكد أن اللاعبين ليسا الشخص نفسه.
في التمرين التالي ستولّد كل الاحتمالات الممكنة لكلمة مرور. أنت تعرف أنها تتكوّن من أربعة أحرف بالضبط، وأن الأحرف المحتملة محدودة ومخزنة هنا:
letters = "ABCD"
اطبع كل كلمات المرور الممكنة:
AAAA
AAAB
AAAC
AAAD
AABA
AABB
... مع تخطي بعض الأسطر ...
DDDA
DDDB
DDDC
DDDD
فكّر في كل موضع من المواضع الأربعة على أنه اختيار مستقل من الأحرف الموجودة في letters.
فكر كيف ستنفذ هذا يدويًا وبنظام باستخدام ورقة وقلم.
كون كلمة المرور من أربعة أحرف بالضبط مهم جدًا؛ لو كان طولها متغيرًا لكان الحل أصعب بكثير.
لكن النص
lettersقد يحتوي أي عدد من المحارف.إذا كان لديك
nأحرف مختلفة، فعدد كلمات المرور الممكنة هوn^4 == n*n*n*nلأن كل موضع لهnاختيارات مستقلة.افترض مجددًا أن
letters = "ABCD". تخيل أن لديك كل كلمات المرور ذات الثلاثة أحرف؛ أضف في النهاية A أو B أو C أو D لكل واحدة لتحصل على كل كلمات المرور ذات الأربعة أحرف.تذكر أن حلقة
forيمكن أن تحتوي أي تعليمة، ومنها حلقةforأخرى.وهذا ينطبق على كل حلقات
for.حلقة داخل حلقة لم تعد كافية.
تحتاج إلى مستوى أعمق.
أصبحت قريبًا من محترفي الاختراق الآن!
تمرين آخر. لديك حجم:
size = 5
اطبع مثلثًا «مقلوبًا» من علامة الجمع +، بحيث يكون طول ضلعه مساويًا للحجم المعطى. مثلًا يجب أن يكون الناتج:
+++++
++++
+++
++
+
فكّر في البرنامج على أنه يطبع أولًا سطرًا بطول size، ثم سطرًا أقصر بحرف واحد، ويستمر حتى يصل إلى حرف واحد.
كيف تصف يدويًا خطوات كتابة هذا المثلث؟
اطبع سطرًا فيه
sizeمن علامات+، ثمsize - 1، ثم استمر حتى علامة+واحدة. مثلًا 5 ثم 4 ثم 3 ثم 2 ثم 1.قسّم المسألة إلى أجزاء أصغر.
كيف تطبع سطرًا من
+بطول محدد؟ وكيف تمر على كل الأطوال المطلوبة؟بناء سطر من المحارف مألوف من التمارين السابقة؛ الفرق هنا أن الطول محدد بدل أن يساوي طول نص آخر.
طريقة سهلة لتنفيذ شيء
nمرات هي المرور علىrange(n).تحتاج إلى حلقة
forداخل حلقةfor.تحتاج إلى أعداد تنازلية مثل 5، 4، 3، 2، 1. يمكنك فعل ذلك بـ
range، أو استخدام نطاق عادي مع حساب بسيط يحول العد التصاعدي إلى تنازلي.ما المعادلة التي تحول 0 إلى 5، و1 إلى 4، و2 إلى 3، وهكذا؟
رائع، لديك حس فني أيضًا!
يمكنك الانتقال إلى الصفحة التالية الآن، أو حل تحدٍّ إضافي.
كما في تمرين البطولة السابق، لديك قائمة بأسماء اللاعبين:
players = ['Charlie', 'Alice', 'Dylan', 'Bob']
هذه المرة يجب أن يظهر كل زوج من اللاعبين مرة واحدة فقط. اطبع الأزواج بالترتيب نفسه من اليسار إلى اليمين كما تظهر الأسماء في players: ابدأ بالأزواج التي تحتوي على أول شخص في القائمة، ثم تحرك يمينًا. في المثال السابق يجب أن يطبع البرنامج:
Charlie vs Alice
Charlie vs Dylan
Charlie vs Bob
Alice vs Dylan
Alice vs Bob
Dylan vs Bob
أي إننا نطبع الزوج فقط عندما يأتي اللاعب الموجود على اليسار قبل اللاعب الموجود على اليمين في القائمة.
ستحتاج إلى حلقة
forداخل حلقةforكما فعلت من قبل.هذه المرة
for player1 in players:وحدها لن تكفي.برنامجك يحتاج إلى استخدام مواضع اللاعبين داخل القائمة.
أي أنك تحتاج إلى المرور على المواضع واستخدام الفهرسة للوصول إلى عناصر القائمة.
للمرور على المواضع، استخدم
range......ومعها
len.انظر إلى الناتج المطلوب:
Charlie vs Alice. يأتيCharlieقبلAliceفي قائمةplayers.لا نريد طباعة
Alice vs CharlieلأنAliceيأتي بعدCharlieفيplayers.نريد فقط الأزواج التي يكون فيها اللاعب الأيسر قبل اللاعب الأيمن في القائمة.
كيف تعبّر عن هذه العلاقة باستخدام فهرسي حلقتي
for؟تحتاج إلى عامل مقارنة.
بعد معرفة العلاقة، عبّر عنها بجملة
if.
ممتاز! أحد الحلول هو:
players = ['Charlie', 'Alice', 'Dylan', 'Bob']
for i in range(len(players)):
for j in range(len(players)):
if i < j:
print(f'{players[i]} vs {players[j]}')