الحلقات المتداخلة في بايثون

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

يوفر هذا البرنامج التعليمي أمثلة عملية وتقنيات تحسين لاستخدام الحلقات المتداخلة بشكل فعال في برامج Python.

البدء باستخدام الحلقات المتداخلة في بايثون

الحلقات البرمجية هي ركائز أساسية في البرمجة، إذ تتيح لك تكرار الإجراءات بكفاءة. في بايثون، يوجد نوعان رئيسيان من الحلقات البرمجية: حلقة for وحلقة while. كلاهما يؤديان نفس الغرض – تنفيذ كتلة من التعليمات البرمجية عدة مرات – لكنهما يختلفان في آلية عملهما وحالات استخدامهما.

  • حلقة for تتكرر على تسلسل، مثل قائمة أو نطاق، وتنفذ كتلة من التعليمات البرمجية لكل عنصر. تُعد هذه الحلقة مفيدة عند معرفة عدد التكرارات مسبقًا.
  • تستمر حلقة while في العمل طالما ظل الشرط المحدد صحيحًا، مما يجعلها مفيدة عندما لا يكون عدد التكرارات معروفًا مسبقًا.

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

في الحلقة المتداخلة، تُسمى الحلقة الأولى الحلقة الخارجية، والحلقة الداخلية هي الحلقة الداخلية. لذا، في كل تكرار للحلقة الخارجية، تُنفذ الحلقة الداخلية بالكامل قبل أن تنتقل الحلقة الخارجية إلى التكرار التالي.

فيما يلي بناء الجملة الأساسي للحلقة المتداخلة:

for outer_variable in outer_iterable:
    for inner_variable in inner_iterable:
        <body>

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

تشبيه جيد للحلقة المتداخلة هو عقربا الساعات والدقائق في الساعة. يتحرك عقرب الساعات ببطء حول الساعة، مُكملاً دورة كاملة كل اثنتي عشرة ساعة. في الوقت نفسه، يتحرك عقرب الدقائق بسرعة أكبر بكثير، مُكملاً دورة كل ساعة. وبينما يدور كلا العقربين بسرعات مختلفة، إلا أنهما يعملان معًا، ويُكمل كل منهما دورته الخاصة في الساعة نفسها.

هكذا يبدو منطق الساعة في كود بايثون:

>>> for hour in range(0, 24):
...     for minute in range(0, 60):
...         print(f"{hour:02d}:{minute:02d}")
...
00:00
00:01
00:02
  ⋮
23:57
23:58
23:59

كما ترى، في كل مرة يكمل فيها عقرب الدقائق دورة، يتحرك عقرب الساعات إلى الساعة التالية. :02d هو محدد تنسيق يضمن طباعة الرقم كقيمة عددية صحيحة مكونة من رقمين.

بعد أن تعرفتَ على الحلقات المتداخلة، حان الوقت لاستكشاف بعض الأمثلة العملية. ستتعرف على كتابة البرامج باستخدام الحلقات المتداخلة في القسم التالي.

استكشاف أمثلة عملية للحلقات المتداخلة

كما تعلمتَ للتو، للحلقات المتداخلة استخداماتٌ متعددة. ستُلقي نظرةً هنا على بعض الأمثلة. هذه الأمثلة شيقة وعملية، تُتيح لك الاستمتاع باستكشاف تركيبها النحوي ودلالاتها.

أنماط الطباعة باستخدام الحلقات المتداخلة

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

>>> height = 6
>>> sail_patterns = "*#-x+o"
>>> for row in range(height):
...     pattern = ""
...     spacing = " " * (height - row)
...     for symbol in sail_patterns:
...         pattern += symbol * row + spacing
...
...     print(pattern)
...

*     #     -     x     +     o
**    ##    --    xx    ++    oo
***   ###   ---   xxx   +++   ooo
****  ####  ----  xxxx  ++++  oooo
***** ##### ----- xxxxx +++++ ooooo

إليك ما يفعله الكود سطرًا بسطر:

  • السطر ١: يُحدِّد مُتغير الارتفاع، وهو مؤشر لحجم السفينة. كما يُتحكَّم في عدد الصفوف في النمط.
  • السطر 3: يبدأ حلقة for الخارجية، ويتكرر من 0 حتى الارتفاع، ولكن لا يشمل الارتفاع.
  • الخط 5: ينقل النمط إلى اليمين، مما يؤدي إلى تقليل المسافات مع زيادة عدد الصفوف.
  • السطر السادس: يبدأ حلقة for الداخلية على أنماط الشراع. ويتبعه نص الحلقة، الذي يُحدِّث النمط.
  • السطر 9: يطبع النمط المكتمل.

بهذه الطريقة، تُبنى المركبة الفضائية قطعةً قطعة. بإضافة قطعةٍ تلو الأخرى، يُمكن زيادة حجمها أو توسيع هيكلها.

إنشاء جدول الضرب

تعرض جداول الضرب نتائج ضرب الأعداد. تُستخدم هذه الجداول عادةً في المدارس الابتدائية لمراجعة سريعة لعمليات الضرب. تُعد حلقات for المتداخلة طريقة رائعة لإنشاء جداول الضرب في بايثون:

>>> for multiplicant in range(1, 11):
...     for multiplier in range(1, 4):
...         expression = f"{multiplicant:>2d} × {multiplier}"
...         product = multiplicant * multiplier
...         print(f"{expression} = {product:>2d}", end="\t")
...     print()
 1 × 1 =  1      1 × 2 =  2      1 × 3 =  3
 2 × 1 =  2      2 × 2 =  4      2 × 3 =  6
 3 × 1 =  3      3 × 2 =  6      3 × 3 =  9
     ⋮                ⋮               ⋮
 8 × 1 =  8      8 × 2 = 16      8 × 3 = 24
 9 × 1 =  9      9 × 2 = 18      9 × 3 = 27
10 × 1 = 10     10 × 2 = 20     10 × 3 = 30

يعرض المثال أعلاه عملية ضرب الأعداد. تتكرر الحلقة الخارجية عبر الأعداد من ١ إلى ١٠، بينما تتكرر الحلقة الداخلية عبر الأعداد من ١ إلى ٣.

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

في القسم التالي، ستكتشف كيفية استخدام الحلقات المتداخلة لجمع كل العناصر في قائمة متداخلة.

جمع العناصر في القوائم متعددة الأبعاد

من تطبيقات الحلقات المتداخلة معالجة العناصر في أكثر من بُعد. القائمة متعددة الأبعاد أو المتداخلة هي قائمة تحتوي على قوائم أخرى كعناصرها. لجمع جميع عناصر القائمة المتداخلة، يمكنك استخدام الحلقات المتداخلة.

بالبقاء على وفاء لموضوع السفر إلى الفضاء، يمكنك تصور مجموع القائمة المتداخلة بهذه الطريقة:

>>> resource_donators = [
...     [8, 6, 3],
...     [9, 2, 7],
...     [4, 1, 5]
... ]
>>> total_resources = 0
>>> for planet in resource_donators:
...     for resource in planet:
...         total_resources += resource
...
>>> print(f"All resources gathered for interstellar travels: {total_resources}")
All resources gathered for interstellar travels: 45

هنا، تستخدم حلقة for متداخلة لمعالجة جمع الموارد من الكواكب الإيثارية في نظام نجمي. تُمثل كل قائمة فرعية في حقل “resource_donators” كوكبًا، ويمثل كل رقم في القائمة الفرعية وحدة من مورد – على سبيل المثال، شيء يُعرف في جميع أنحاء المجرة باسم foodonium. قيمة إجمالي الموارد مُهيأة إلى 0.

تتكرر الحلقة الخارجية عبر كل كوكب، بينما تمر الحلقة الداخلية عبر كل مورد على حدة، وتضيفه إلى إجمالي الموارد. لنفترض أن أحد الكواكب يوفر 8 و6 و3 وحدات من فودونيوم. بعد معالجة جميع الكواكب، يُطبع الإجمالي النهائي، ويمكن للمركبة الفضائية مواصلة رحلتها.

بعد ذلك، ستلقي نظرة على كيفية استخدام الحلقات المتداخلة لإنشاء مجموعات زوجية.

إنشاء مجموعات زوجية

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

>>> players = ["Bonnie", "Mike", "Raj", "Adah"]

>>> for player1 in players:
...     for player2 in players:
...         print(f"{player1} vs {player2}")
...
Bonnie vs Bonnie
Bonnie vs Mike
Bonnie vs Raj
      ⋮
Adah vs Bonnie
Adah vs Mike
Adah vs Raj
Adah vs Adah

باستخدام هذا النهج، يمكنك إنشاء جميع المواجهات الممكنة – بما في ذلك المواجهات الذاتية – مما يُظهر إمكانية تكوين مجموعات ثنائية باستخدام الحلقات المتداخلة. ربما لاحظتَ أنه يُسمح للاعبين باللعب ضد أنفسهم، وهو أمر قد لا ترغب فيه.

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

العمل مع حلقات while في الهياكل المتداخلة

حتى الآن، رأيتَ العديد من الأمثلة على استخدام حلقات for في حلقات متداخلة. هناك أيضًا عدد من الحالات التي تُفضّل فيها استخدام حلقات while بدلاً منها. على سبيل المثال، لنفترض أنك تُنشئ تطبيقًا يطبع كل حرف من كلمة:

>>> while True:
...     word = input("Enter a word (or type 'exit' to stop): ")
...
...     if word == "exit":
...         break
...
...     for letter in word:
...         print(letter)
...
Enter a word (or type 'exit' to stop): foodonium
f
o
o
d
o
n
i
u
m
Enter a word (or type 'exit' to stop): exit
>>>

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

سواءً كنت تستخدم حلقات for أو حلقات while في حلقات متداخلة، عليك توخي الحذر عند استخدامها. سوء الاستخدام قد يؤدي إلى عدد من المشاكل، مثل ضعف قابلية القراءة ومشاكل في الأداء، كما سترى قريبًا.

تجنب الأخطاء الشائعة في الحلقات المتداخلة

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

  • قضايا نطاق المتغيرات
  • ضعف قابلية القراءة
  • اختناقات الأداء

في الأقسام التالية، ستلقي نظرة عن كثب على كل من هذه الأخطاء وتبحث عن طرق لمنعها.

التعامل مع مشكلات نطاق المتغيرات

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

إذا نسيتَ تسجيل هذه الحالات، فقد تضطر إلى التعامل مع الأخطاء لاحقًا، مما قد يُسبب لك متاعب كبيرة. انظر إلى الكود التالي:

>>> employees = [("Dorothy", "DevOps"), ("Abdel", "HR"), ("Nataliya", "DevOps")]
>>> departments = [
...     {"name": "DevOps", "city": "Berlin"},
...     {"name": "HR", "city": "Abuja"},
... ]

>>> for name, department in employees:
...     for department in departments:
...         if department["name"] == department:
...             print(f"{name} works in {department['city']}")
...

ما ستلاحظه هنا أولًا هو أن كلمة “department” تشير إلى اسم القسم المرتبط بالموظف، كما هو مُعرّف في حلقة “for” الخارجية. ومع ذلك، فإن حلقة “for” الداخلية تُشير أيضًا إلى department، وهو قيمة في قائمة قواميس. يتم استبدال التعريف الأصلي لكلمة “department” في الحلقة الخارجية بالحلقة الداخلية، مما يُسبب أخطاءً منطقية.

إحدى الطرق لحل هذه المشكلة هي إعلان أسماء متغيرات مختلفة للحلقات الداخلية والخارجية:

>>> for name, department in employees:
...     for dept in departments:
...         if dept["name"] == department:
...             print(f"{name} works in {dept['city']}")
...
Dorothy works in Berlin
Abdel works in Abuja
Nataliya works in Berlin

الآن يمكنك التأكد من التعامل مع department وdept بشكل منفصل. لتجنب مثل هذه المشاكل، يجب عليك دائمًا تجنب إعادة استخدام اسم المتغير نفسه في كلٍّ من الحلقتين الخارجية والداخلية، وتوخي الحذر عند تعديل متغير حلقة خارجية داخل حلقة داخلية.

معالجة ضعف قابلية القراءة

من التحديات الرئيسية عند العمل مع الحلقات المتداخلة ضعف قابلية القراءة. مع ازدياد تعقيد البرنامج، قد تُصعّب الحلقات المتداخلة قراءة الكود وصيانته. مع أن الحلقات المتداخلة ضرورية أحيانًا، إلا أن الإفراط في التداخل قد يُصعّب على المطورين الآخرين – أو حتى عليك – فهم ما يجري في الكود.

فكر في المثال التالي، وهو بروتوكول بحث عندما يغامر أحد المتسللين بالدخول إلى سفينتك:

>>> living_quarters = 3
>>> sections = 2
>>> floors = 3

>>> for floor in range(floors):
...     for section in range(sections):
...         for living_quarter in range(living_quarters):
...             print(
...                 f"Scanning quarter {living_quarter} in section {section}"
...                 f" on floor {floor} for the intruder..."
...             )
...
Scanning quarter 0 in section 0 on floor 0 for the intruder...
Scanning quarter 1 in section 0 on floor 0 for the intruder...
Scanning quarter 2 in section 0 on floor 0 for the intruder...
...
Scanning quarter 0 in section 1 on floor 2 for the intruder...
Scanning quarter 1 in section 1 on floor 2 for the intruder...
Scanning quarter 2 in section 1 on floor 2 for the intruder...

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

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

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

من الاعتبارات المهمة الأخرى المتعلقة بالحلقات المتداخلة تأثيرها على الأداء. ستتناول هذا لاحقًا.

التعامل مع اختناقات الأداء

لفهم كيفية تأثير الحلقات المتداخلة المكتوبة بشكل سيء على الأداء، من المفيد استكشاف مفهوم التعقيد الزمني بإيجاز. التعقيد الزمني هو طريقة لقياس سرعة تشغيل الخوارزمية بالنسبة لمعدل نمو حجم المدخلات، ويُرمز له بـ O(n).

يُمثل الرمز O ترميز Big O، حيث n هو حجم المُدخلات. وهو مقياس شائع الاستخدام للأداء، مُصنف حسب تعقيدات مُرتبة من الأكثر كفاءة إلى الأقل كفاءة:

التعقيدBig Oالوصف
الوقت الثابتO(1)لا يتغير وقت التشغيل مع حجم الإدخال
الوقت اللوغاريتميO(log n)ينمو وقت التشغيل ببطء مع زيادة حجم الإدخال
الزمن الخطيO(n)ينمو وقت التشغيل بشكل مباشر مع حجم الإدخال
الوقت التربيعيO(n2)ينمو وقت التشغيل مع مربع حجم الإدخال
الزمن الأسّيO(2n)يتضاعف وقت التشغيل مع كل إدخال إضافي
الوقت العامليO(n!)ينمو وقت التشغيل بسرعة كبيرة (النمو العاملي)

حلقة for واحدة تعمل في زمن O(n). قد لا يبدو هذا سيئًا للأداء، لكن تخيل وجود ست طبقات من الحلقات المتداخلة. في هذه الحالة، يكون التعقيد الزمني O(c × r × a × w × l)، حيث يمثل كل حرف حلقة. سيؤدي ذلك إلى اختناقات في الأداء. وهكذا، يمكنك أن ترى لماذا تصبح الحلقات المتداخلة غير فعالة بسرعة، خاصةً في البرامج الكبيرة حيث تزيد كل حلقة من التعقيد الزمني.

يوضح المثال أدناه خوارزمية لاكتشاف المستنسخين الهاربين على متن سفينتك – وهو أحد الآثار الجانبية المؤسفة لتجربة علمية حسنة النية:

>>> crew_ids = [1, 2, 8, 4, 5, 6, 7, 8, 9, 10]
>>> for i, first_id in enumerate(crew_ids):
...     for second_id in crew_ids[i + 1:]:
...         if first_id == second_id:
...             print(f"Clone found: id={first_id} is a duplicate.")
...
Clone found: id=8 is a duplicate.

كما ترى، هناك حلقتان فقط، لذا فإن تعقيد الوقت قد يكون O(c × r) ويشار إليه بـ O(n2).

في مثال صغير كهذا، قد تنجح، لكن مع إدخال كبير، سيؤثر ذلك سلبًا على الأداء. سيساعدك الاهتمام بالأداء على كتابة خوارزميات أكثر كفاءة.

هذا الكود يحوي قصة ممتعة لتحديد النسخ المكررة على متن سفينة. لكن في الواقع، هو يحدد أرقامًا مكررة في قائمة. يفحص كل معرف أولي واحدًا تلو الآخر، مع تتبع موقعه باستخدام دالة ()enumerate. إذا تطابق أي من الأرقام في القائمة، فسيتم تحديد الرقم كنسخة مكررة. والأهم من ذلك، سيتم التقاط نسخة مكررة.

قد يكون حل هذه المشكلة هو الاستغناء عن الحلقات المتداخلة وتفضيل تقنيات أكثر تحسينًا. في المثال أعلاه، يمكنك استخدام دالة Python set أو collections.Counter بدلًا من الحلقات المتداخلة. في كلتا الحالتين، يبلغ التعقيد الزمني O(n)، وهو أداء أفضل من O(n²).

في الكود أدناه، يمكنك رؤية كيفية عمل ذلك مع مجموعة:

>>> crew_ids = [1, 2, 8, 4, 5, 6, 7, 8, 9, 10]
>>> detected = set()
>>> for crew_id in crew_ids:
...     if crew_id in detected:
...         print(f"Clone found: id={crew_id} is a duplicate.")
...     else:
...         detected.add(crew_id)
...
Clone found: id=8 is a duplicate.

يوضح المثال التالي كيفية اكتشاف التكرارات باستخدام Counter:

>>> from collections import Counter
>>> crew_ids = [1, 2, 8, 4, 5, 6, 7, 8, 9, 10]
>>> detected = Counter(crew_ids)
>>> for key, value in detected.items():
...     if value > 1:
...         print(f"Clone found: id={key} is a duplicate.")
...
Clone found: id=8 is a duplicate.

هنا، يكتشف Counter التكرارات نيابةً عنك. تذكر أنه ليس عليك التخلي عن الحلقات المتداخلة تمامًا في بعض الحالات. يمكن تحسين الحلقات المتداخلة، وستتعلم كيفية القيام بذلك بالتفصيل لاحقًا.

تحسين الحلقات المتداخلة في بايثون

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

استخدام break وcontinue

قد يؤدي استخدام الحلقات المتداخلة أحيانًا إلى تكرارات غير ضرورية. يمكنك التحكم في هذه التكرارات بشكل أفضل باستخدام عبارات break وcontinue.

إذا كانت حلقة for المتداخلة عبارة عن شطيرة BLT، فإن استخدام break يُشبه تناول الطبقات العليا والتخلص من الباقي. أما استخدام continue، فيشبه تناول الطبقات العليا، ثم تخطي الخس، ثم إنهاء الطبقات السفلى. تتخطى عبارة continue التكرارات غير الضرورية، مما يُقلل من عبء العمل ويُحسّن الأداء.

بمعنى آخر، تتيح لك عبارة break الخروج من الحلقة قبل انتهائها، بينما تتيح لك عبارة continue تخطي التكرارات غير الضرورية. لو كنت رائد فضاء يسبح في انعدام الجاذبية ويكره الخس، فقد يبدو برنامج لكيفية تناول شطيرة BLT ضخمة كما يلي:

>>> blt_sandwich = [
...     ["bread", "lettuce", "tomato", "bacon"],
...     ["bread", "bacon", "lettuce", "tomato"],
...     ["bacon", "bacon", "tomato", "lettuce"]
... ]
>>> target = "bacon"
>>> found = False
>>> for layer in blt_sandwich:
...     for ingredient in layer:
...         if ingredient == target:
...             print(f"Found the crispy {target}!")
...             found = True
...             break
...
...     if found:
...         print("Enjoying the crunch and worth it.")
...         break
...
Found the crispy bacon!
Enjoying the crunch and worth it.

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

يمكن تحسين حلقات for المتداخلة باستخدام continue كما في هذا المثال:

>>> blt_sandwich = [
...     ["bread", "lettuce", "tomato", "bacon"],
...     ["bread", "bacon", "lettuce", "tomato"],
...     ["bacon", "bacon", "tomato", "lettuce"]
... ]
>>> target = "bacon"
>>> found = False
>>> for layer in blt_sandwich:
...     for ingredient in layer:
...         if ingredient != target:
...             print(f"This is not bacon. Skipping...")
...             continue
...         print(f"Found the crispy {target}!")
...         found = True
...         break
...
...     if found:
...         print("Enjoying the crunch and worth it.")
...         break
...
This is not bacon. Skipping...
This is not bacon. Skipping...
This is not bacon. Skipping...
Found the crispy bacon!
Enjoying the crunch and worth it.

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

استبدال الحلقات المتداخلة بفهم القائمة

باستخدام فهم القوائم، يمكنك استبدال سلسلة الحلقات المتداخلة المزعجة بتعبير أكثر إحكامًا وسهولة في القراءة. مع أن فهم القوائم لا يُحسّن الأداء بالضرورة أو يُقلل من تعقيد الوقت – خاصةً في التكرارات المتداخلة – إلا أنه غالبًا ما يُوضّح هدفك ويُسهّل فهم الكود.

يوضح المثال التالي هذا الأمر مع أحد أفراد الطاقم الذي قرر صنع أكوام ضخمة من الفطائر:

>>> bases = ["plain", "chocolate", "blueberry"]
>>> toppings = ["honey", "whipped cream", "alien syrup"]
>>> pancake_stacks = []
>>> for base in bases:
...     for topping in toppings:
...         pancake_stacks.append(f"{base.capitalize()} pancake with {topping}")
...
... print(pancake_stacks)
...
['Plain pancake with honey',
'Plain pancake with whipped cream',
'Plain pancake with alien syrup',
'Chocolate pancake with honey',
'Chocolate pancake with whipped cream',
'Chocolate pancake with alien syrup',
'Blueberry pancake with honey',
'Blueberry pancake with whipped cream',
'Blueberry pancake with alien syrup']

هذا مثال على حلقة for متداخلة تقليدية. ستلاحظ أن الحلقات الخارجية والداخلية تستخدم أسماء متغيرات مختلفة. هذه تفصيلة مهمة تساعدك على تجنب مشاكل تحديد نطاق المتغيرات التي تعلمتها سابقًا.

ستجد أدناه نفس البرنامج، ولكن هذه المرة باستخدام فهم القائمة للحلقات المتداخلة:

>>> bases = ["plain", "chocolate", "blueberry"]
>>> toppings = ["honey", "whipped cream", "alien syrup"]
>>> pancake_stacks = [
...     f"{base.capitalize()} pancake with {topping}"
...     for base in bases
...     for topping in toppings
... ]
>>> print(pancake_stacks)
['Plain pancake with honey',
'Plain pancake with whipped cream',
'Plain pancake with alien syrup',
'Chocolate pancake with honey',
'Chocolate pancake with whipped cream',
'Chocolate pancake with alien syrup',
'Blueberry pancake with honey',
'Blueberry pancake with whipped cream',
'Blueberry pancake with alien syrup']

لاحظ أنه على الرغم من أن فهم القوائم قد يبدو واضحًا وموجزًا، إلا أنه ليس الخيار الأفضل دائمًا. قد تتأثر سهولة القراءة عندما يصبح المنطق معقدًا للغاية، خاصةً مع الهياكل المتداخلة. وهذا يتعارض مع فلسفة بايثون، التي تؤكد على أهمية سهولة القراءة.

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

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

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

يساعدك فهم الحلقات المتداخلة على كتابة تعليمات برمجية أكثر كفاءة وإدارة هياكل البيانات المعقدة وتجنب الأخطاء الشائعة مثل ضعف القراءة ومشاكل الأداء.


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading