الدرس 7

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

الوحدة 2: التحكم في التدفق وقت القراءة: 15 دقيقة

جملة pass

جملة pass هي عملية فارغة - لا تفعل شيئاً. تفيد كعنصر نائب عندما تحتاج جملة نحوياً لكن ليس لديك كود بعد.

Python
# pass كعنصر نائب
for ع in range(5):
    pass  # TODO: سيُنفذ لاحقاً

# مفيد في تعريفات الفئات/الدوال الفارغة
class فئتي:
    pass  # سأضيف الدوال لاحقاً

def دالتي():
    pass  # التنفيذ قيد الانتظار

# تجاهل حالات معينة
for رقم in range(10):
    if رقم % 2 == 0:
        pass  # تخطي الأرقام الزوجية (لا تفعل شيئاً)
    else:
        print(رقم)

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

break و continue تؤثر فقط على الحلقة الداخلية:

Python
# break تخرج فقط من الحلقة الداخلية
for ص in range(3):
    print(f"الحلقة الخارجية: {ص}")
    for س in range(3):
        if س == 1:
            break  # تخرج فقط من الحلقة الداخلية
        print(f"  الحلقة الداخلية: {س}")

# الناتج:
# الحلقة الخارجية: 0
#   الحلقة الداخلية: 0
# الحلقة الخارجية: 1
#   الحلقة الداخلية: 0
# الحلقة الخارجية: 2
#   الحلقة الداخلية: 0

# استخدام علم للخروج من الحلقة الخارجية
وجدت = False
for ص in range(5):
    for س in range(5):
        if ص * س == 6:
            print(f"وجدت: {ص} × {س} = 6")
            وجدت = True
            break
    if وجدت:
        break

# استخدام else مع الحلقات المتداخلة
for ص in range(3):
    for س in range(3):
        if ص == 1 and س == 1:
            break
    else:
        # هذا يعمل إذا لم تنكسر الحلقة الداخلية
        print(f"الحلقة الداخلية اكتملت لـ ص={ص}")
        continue
    # هذا يعمل إذا انكسرت الحلقة الداخلية
    print(f"الحلقة الداخلية انكسرت لـ ص={ص}")

الخروج من الحلقات المتداخلة

عدة تقنيات للخروج من حلقات متعددة:

Python
# الطريقة 1: استخدام دالة مع return
def ابحث_عن_هدف(مصفوفة, هدف):
    for ص, صف in enumerate(مصفوفة):
        for س, قيمة in enumerate(صف):
            if قيمة == هدف:
                return (ص, س)  # تخرج من كامل الدالة
    return None

مصفوفة = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

نتيجة = ابحث_عن_هدف(مصفوفة, 5)
print(f"وجدت في: {نتيجة}")  # وجدت في: (1, 1)

# الطريقة 2: استخدام استثناء (للحالات المعقدة)
class اخرج_من_الحلقة(Exception):
    pass

try:
    for ص in range(5):
        for س in range(5):
            for ع in range(5):
                if ص * س * ع == 24:
                    print(f"وجدت: {ص}, {س}, {ع}")
                    raise اخرج_من_الحلقة
except اخرج_من_الحلقة:
    pass

# الطريقة 3: استخدام itertools.product
from itertools import product

for ص, س, ع in product(range(5), range(5), range(5)):
    if ص * س * ع == 24:
        print(f"وجدت: {ص}, {س}, {ع}")
        break

أنماط الحلقات العملية

النمط 1: المُجمِّع

Python
# جمع الأرقام
أرقام = [1, 2, 3, 4, 5]
المجموع = 0
for رقم in أرقام:
    المجموع += رقم
print(f"المجموع: {المجموع}")  # 15

# بناء نص
كلمات = ["مرحباً", "بالعالم", "بايثون"]
النتيجة = ""
for كلمة in كلمات:
    النتيجة += كلمة + " "
print(النتيجة.strip())  # مرحباً بالعالم بايثون

# عد التكرارات
نص = "مسيسيبي"
العد = {}
for حرف in نص:
    العد[حرف] = العد.get(حرف, 0) + 1
print(العد)  # {'م': 1, 'س': 2, 'ي': 3, 'ب': 1}

النمط 2: البحث والتصفية

Python
# إيجاد أول تطابق
أرقام = [3, 7, 2, 8, 1, 9, 5]
أول_زوجي = None

for رقم in أرقام:
    if رقم % 2 == 0:
        أول_زوجي = رقم
        break

print(f"أول زوجي: {أول_زوجي}")  # 2

# إيجاد جميع التطابقات
أرقام_زوجية = []
for رقم in أرقام:
    if رقم % 2 == 0:
        أرقام_زوجية.append(رقم)
print(f"جميع الأزواج: {أرقام_زوجية}")  # [2, 8]

# باستخدام فهم القائمة
أرقام_زوجية = [رقم for رقم in أرقام if رقم % 2 == 0]
print(f"جميع الأزواج: {أرقام_زوجية}")  # [2, 8]

# تصفية الكائنات
طلاب = [
    {"الاسم": "أحمد", "الدرجة": 90},
    {"الاسم": "سارة", "الدرجة": 55},
    {"الاسم": "محمد", "الدرجة": 75},
    {"الاسم": "ليلى", "الدرجة": 45}
]

الناجحون = [ط for ط in طلاب if ط["الدرجة"] >= 60]
print("الناجحون:", [ط["الاسم"] for ط in الناجحون])  # ['أحمد', 'محمد']

النمط 3: تحويل البيانات

Python
# تحويل التخطيط (Map)
أرقام = [1, 2, 3, 4, 5]
مربعات = []
for رقم in أرقام:
    مربعات.append(رقم ** 2)
print(مربعات)  # [1, 4, 9, 16, 25]

# باستخدام فهم القائمة
مربعات = [رقم ** 2 for رقم in أرقام]
print(مربعات)  # [1, 4, 9, 16, 25]

# تحويل القاموس
أسعار = {"تفاح": 10.0, "موز": 5.0, "كرز": 20.0}
مخفضة = {}
for صنف, سعر in أسعار.items():
    مخفضة[صنف] = سعر * 0.9  # خصم 10%
print(مخفضة)

# باستخدام فهم القاموس
مخفضة = {صنف: سعر * 0.9 for صنف, سعر in أسعار.items()}
print(مخفضة)

النمط 4: التكرار المتوازي

Python
# استخدام zip للتكرار المتوازي
أسماء = ["أحمد", "سارة", "محمد"]
أعمار = [25, 30, 35]
مدن = ["الرياض", "جدة", "الدمام"]

for اسم, عمر, مدينة in zip(أسماء, أعمار, مدن):
    print(f"{اسم}، {عمر}، من {مدينة}")

# zip تتوقف عند الأقصر
قصيرة = [1, 2]
طويلة = [1, 2, 3, 4, 5]
for أ, ب in zip(قصيرة, طويلة):
    print(أ, ب)  # يطبع زوجين فقط

# zip_longest للأطوال المختلفة
from itertools import zip_longest
for أ, ب in zip_longest(قصيرة, طويلة, fillvalue=0):
    print(أ, ب)  # يطبع 5 أزواج، يملأ بـ 0

# إنشاء قاموس من قائمتين
مفاتيح = ["الاسم", "العمر", "المدينة"]
قيم = ["أحمد", 25, "الرياض"]
شخص = dict(zip(مفاتيح, قيم))
print(شخص)  # {'الاسم': 'أحمد', 'العمر': 25, 'المدينة': 'الرياض'}

النمط 5: النافذة المنزلقة

Python
# معالجة أزواج متتالية
أرقام = [1, 2, 3, 4, 5]

for ع in range(len(أرقام) - 1):
    الحالي = أرقام[ع]
    التالي = أرقام[ع + 1]
    print(f"{الحالي} -> {التالي}")

# باستخدام zip للأزواج
for الحالي, التالي in zip(أرقام, أرقام[1:]):
    print(f"{الحالي} -> {التالي}")

# المتوسط المتحرك (نافذة من 3)
بيانات = [10, 20, 30, 40, 50, 60, 70]
حجم_النافذة = 3

متوسطات = []
for ع in range(len(بيانات) - حجم_النافذة + 1):
    نافذة = بيانات[ع:ع + حجم_النافذة]
    متوسط = sum(نافذة) / حجم_النافذة
    متوسطات.append(متوسط)

print(f"المتوسطات المتحركة: {متوسطات}")
# [20.0, 30.0, 40.0, 50.0, 60.0]

نصائح أداء الحلقات

Python
# النصيحة 1: انقل الحسابات خارج الحلقة
قائمة = list(range(1000))

# سيء
for ع in range(1000):
    نتيجة = len(قائمة) * ع  # len() تُستدعى 1000 مرة

# جيد
طول_القائمة = len(قائمة)
for ع in range(1000):
    نتيجة = طول_القائمة * ع  # len() تُستدعى مرة واحدة

# النصيحة 2: استخدم هياكل البيانات المناسبة
# سيء - O(n) بحث في كل مرة
عناصر = [1, 2, 3, 4, 5]
for ع in range(1000):
    if ع in عناصر:  # بحث خطي
        pass

# جيد - O(1) بحث
مجموعة_عناصر = {1, 2, 3, 4, 5}
for ع in range(1000):
    if ع in مجموعة_عناصر:  # بحث بالتجزئة
        pass

# النصيحة 3: استخدم الدوال المدمجة
أرقام = list(range(10000))

# أبطأ
المجموع = 0
for رقم in أرقام:
    المجموع += رقم

# أسرع
المجموع = sum(أرقام)

# النصيحة 4: فهم القائمة vs الحلقة
# أبطأ قليلاً (استدعاء دالة)
نتيجة = []
for س in range(1000):
    نتيجة.append(س ** 2)

# أسرع
نتيجة = [س ** 2 for س in range(1000)]
متى تُحسِّن

لا تُحسِّن قبل الأوان! اكتب كوداً واضحاً وقابلاً للقراءة أولاً. حسِّن فقط عندما تحدد عنق زجاجة في الأداء من خلال التحليل.

تحدي: مُدقق كلمة المرور

اكتب دالة تتحقق من صحة كلمة المرور:

  • 8 أحرف على الأقل
  • تحتوي على حرف كبير واحد على الأقل
  • تحتوي على حرف صغير واحد على الأقل
  • تحتوي على رقم واحد على الأقل
  • تحتوي على رمز خاص واحد على الأقل (!@#$%^&*)
أظهر الحل
def تحقق_كلمة_السر(كلمة_السر):
    # تحقق من الطول
    if len(كلمة_السر) < 8:
        return False, "كلمة السر يجب أن تكون 8 أحرف على الأقل"

    لديه_كبير = False
    لديه_صغير = False
    لديه_رقم = False
    لديه_خاص = False
    الرموز_الخاصة = "!@#$%^&*"

    for حرف in كلمة_السر:
        if حرف.isupper():
            لديه_كبير = True
        elif حرف.islower():
            لديه_صغير = True
        elif حرف.isdigit():
            لديه_رقم = True
        elif حرف in الرموز_الخاصة:
            لديه_خاص = True

    # تحقق من جميع المتطلبات
    if not لديه_كبير:
        return False, "ينقص حرف كبير"
    if not لديه_صغير:
        return False, "ينقص حرف صغير"
    if not لديه_رقم:
        return False, "ينقص رقم"
    if not لديه_خاص:
        return False, "ينقص رمز خاص"

    return True, "كلمة السر صالحة"

# اختبار
كلمات_سر = ["ضعيف", "StrongPass1!", "NoSpecial1", "nouppercase1!"]

for كلمة in كلمات_سر:
    صالح, رسالة = تحقق_كلمة_السر(كلمة)
    print(f"{كلمة}: {رسالة}")

تحدي: عمليات المصفوفة

أنشئ دوال لـ:

  1. إيجاد مجموع جميع العناصر في مصفوفة
  2. إيجاد أكبر عنصر وموقعه
  3. تبديل صفوف المصفوفة بأعمدتها
أظهر الحل
def مجموع_المصفوفة(مصفوفة):
    المجموع = 0
    for صف in مصفوفة:
        for قيمة in صف:
            المجموع += قيمة
    return المجموع

def ابحث_عن_الأكبر(مصفوفة):
    أكبر_قيمة = مصفوفة[0][0]
    موقع_الأكبر = (0, 0)

    for ص, صف in enumerate(مصفوفة):
        for س, قيمة in enumerate(صف):
            if قيمة > أكبر_قيمة:
                أكبر_قيمة = قيمة
                موقع_الأكبر = (ص, س)

    return أكبر_قيمة, موقع_الأكبر

def التبديل(مصفوفة):
    الصفوف = len(مصفوفة)
    الأعمدة = len(مصفوفة[0])
    النتيجة = []

    for س in range(الأعمدة):
        صف_جديد = []
        for ص in range(الصفوف):
            صف_جديد.append(مصفوفة[ص][س])
        النتيجة.append(صف_جديد)

    return النتيجة

# اختبار
مصفوفة = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print(f"المجموع: {مجموع_المصفوفة(مصفوفة)}")  # 45
print(f"الأكبر: {ابحث_عن_الأكبر(مصفوفة)}")    # (9, (2, 2))
print(f"التبديل: {التبديل(مصفوفة)}")
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

النقاط الرئيسية

ملخص
  • pass عنصر نائب لا يفعل شيئاً
  • break و continue تؤثر فقط على الحلقة الداخلية
  • استخدم الدوال أو الأعلام للخروج من الحلقات المتداخلة
  • الأنماط الشائعة: المُجمِّع، البحث/التصفية، التحويل، التكرار المتوازي
  • استخدم zip() للتكرار المتوازي على تسلسلات متعددة
  • فهم القوائم غالباً أسرع من الحلقات التقليدية
  • حسِّن فقط بعد تحديد عنق الزجاجة الفعلي

في الدرس القادم، سنستكشف هياكل البيانات بدءاً بالقوائم!