3  الشرط والتكرار

يسير تنفيذ الجمل البرمجية في اللغات الإجرائية -مثل بايثون- من الأعلى للأسفل. فإذا كتبنا الكود التالي:

flowchart TD
    A[x = 1] --> B[x = 2] --> C["print(x)"]

x = 1
x = 2
print(x)
2

فإن جملة التعيين التالية تكتُب قيمة 2 في نفس المحلّ الذي كتبت عليه جملة التعيين الأولى 1. لذا ظهرت النتيجة: 2.

كثيرًا ما نحتاج للتحكم فيما يُنفَّذ وما يُهمل أو ما يتكرر من الجمل البرمجية. فمن الحالات التي يتغير فيها الترتيب:

  1. التنفيذ المشروط: تعليق تنفيذ قطعة كود على حصول حالة (أو أحوال) معيَّنة
  2. التكرار: تكرار تنفيذ قطعة كود عددًا من المرات أو معلَّقًا بتمام شرط

3.1 الجملة الشرطية

الجملة الشرطية هي جملة مركَّبة من كلمة if وتعبير منطقي ثم الجمل التي يتعلق تنفيذها بناءً على هذا الشرط (وتكون داخل المحاذاة):

flowchart TD
    IF{{if cond}} -- False --> X["Outside"]
    IF -- True --> Y["Inside"]

  style IF fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 0 color:red;

cond = True
if cond:
    print('Inside')
print('Outside')
Inside
Outside

تنبيه: المحاذاة العمودية

لاحظ أن المحاذاة العمودية (Indentation) (المسافات البيضاء أسفل كلمة if) في الكود أعلاه ليست لمجرد تسهيل قراءة الكود، بل هي التي تحدد التعليمات المشروطة. عادةً ما تكون المحاذاة عبارة عن 4 مسافات أو 2 أو أكثر، لكن لابد أن تكون موحَّدة طوال الكود.

لاحظ: بدون المحاذاة الصحيحة، سيظهر خطأ في الكود:

flowchart TD
    IF{{if True}}
    Y["Inside"] --> X["Outside"]
    style IF fill:#fc0000, stroke:#333, stroke-width:2px;

if True:
print('Inside')
print('Outside')
  Cell In[3], line 2
    print('Inside')
    ^
IndentationError: expected an indented block after 'if' statement on line 1

إذا قمت بزيادة المحاذاة لكل من جملتي print()، فسوف يعمل الكود بتدفِّقٍ غيرِ الذي قصدناه ابتداءً. أي أنه سيطبع Outside عندما يكون في الواقع داخل اللَّبِنَة الشرطية.

flowchart TD
    IF{{if True}}
    IF -- True --> Y["Inside"] --> X["Outside"]
    
    style IF fill:#82aeff, stroke:#333, stroke-width:2px;

if True:
    print('Inside')
    print('Outside')
Inside
Outside

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

3.2 التعبيرات المنطقية

أما الشروط فتستند إلى الجبر الثنائي (Boolean Logic) الذي نجد له في بايثون كلمتين من أصل اللغة هما:

  1. كلمة True ويعبَّر عنها بالرقم: 1
  2. كلمة False ويعبَّر عنها بالرقم: 0

وكلاهُما يندرج تحت نوع خاصّ من نوع الأرقام وهو النوع البولي (bool) نسبةً لعالم الرياضيات جورج بول الذي وضع أسسه.

أما الجملة الشرطية أو التعيين الشرطي ونحوه، فيتعلَّق بتحقق عبارة منطقية. فمن العبارات المنطقية: عبارة المقارنة:

العلامة الوصف
a == b يساوي
a != b لا يساوي
a > b أكبر من
a < b أصغر من
a >= b أكبر من أو يساوي
a <= b أصغر من أو يساوي

هنا نستكشف عبارات تؤول إلى قيَم منطقية. العبارة الأولى: خمسة أكبر من تسعة؟

b1 = 5 > 9
print(type(b1))
print(b1)
<class 'bool'>
False

العبارة الثانية: هل طول كذا أكبر من طول كذا؟

b2 = len('12345') > len('123456789')
print(b2)
False

تركيب الشروط

يجوز دمج عدة شروط بعمليات الجمع والتخيير والحصر والعكس، فناتجها منطقي ملخص في الجدول التالي:

A B AND OR XOR
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
  • الجمع: AND (و): تخرج 1 فقط إذا كان كلا المدخلين 1.
  • التخيير: OR (أو): تخرج 1 إذا كان أحد المدخلين على الأقل 1.
  • الحصر: XOR (أو الحصرية): تخرج 1 إذا كان المدخلان مختلفين.
  • العكس: NOT (ليس): تخرج عكس المدخل (1 يصبح 0، و0 يصبح 1).

على سبيل المثال:

age = 20
weight = 50

if age > 18 and weight > 45:
  print("You are eligible to donate blood")
You are eligible to donate blood

يؤول طرفي المعامل and لقيمة منطقية هكذا:

  • age > 18 تؤول إلى True لأن age=20 وهو أكبر من 18
  • weight > 45 تؤول إلى True لأن weight=50 وهو أكبر من 45
  • فتكون الجملة إذًا: True and True وهي تؤول إلى True

تستعمل الأقواس لتجميع الشروط لإيقاع الترتيب المنطقي المراد:

age = 16
temperature = 15
is_wearing_coat = True

if age < 16 or (temperature < 20 and not is_wearing_coat):
  print("I suggest you don't go outside")
  • age < 16 تؤول إلى False لأن age=16 ليست أصغر من 16
  • temperature < 20 تؤول إلى True لأن temperature=15 أصغر من 20 فعلاً
  • not is_wearing_coat تؤول إلى False لأن is_wearing_coat=True وهي عكسها
  • إذًا الجملة بين القوسين (temperature < 20 and not is_wearing_coat) تؤول إلى True and False وهي تؤول إلى False
  • إذًا الجملة age < 16 or (False) تؤول إلى False or False وهي تؤول إلى False

يُنظَر للشرط بأكمله كقيمة منطقية واحدة تكون True أو False ولا بأس بتجزئته حينما يسهل بذلك الفهم:

is_minor = age < 16
is_cold = temperature < 20

if is_minor or (is_cold and not is_wearing_coat):
  print("I suggest you don't go outside")

تسلسل المقارنات

تفهم بايثون المقارنات المتسلسلة. فعبارة x < y <= z تكافئ x < y and y <= z:

low = 10
high = 20
x = 15

assert (low < x < high) == (low < x and x < high)

كذلك تراها تستعمل في المساواة:

assert 3 == len('123') == len([10, 20, 30]) == len('abc')

3.3 الجملة الشرطية المتكاملة

الصيغة المتكاملة للجملة الشرطية على النحو التالي:

flowchart TD
    IF{{if condition_1}}
    IF -- False --> ELIF{{elif condition_2}}
    IF -- True --> code_1
    ELIF -- False --> ELSE{{else}}
    ELIF -- True --> code_2
    ELSE --> code_3

    style IF fill:#82aeff, stroke:#333, stroke-width:2px;
    style ELIF fill:#82aeff, stroke:#333, stroke-width:2px;
    style ELSE fill:#82aeff, stroke:#333, stroke-width:2px;
    linkStyle 0,2 color:red;

if <boolean expression>:
    <code>
elif <boolean expression>:
    <code>
else:
    <code>
  • كلمة if (إذا) تبدأ الجملة المركبة الشرطية
  • قد توجد else (وإلا) مرة. وهي تعمل عند تخلُّف العبارة المنطقية السابقة لها سواءً كانت السابقة لها if أو elif.
  • وقد توجد elif بينهما مرة أو أكثر (وهي اختصار لكلمة else if وتعني: وإلا فإن)، فتعمل مثل else معلَّقة بعبارة منطقية مثل if.

جرب

استكشف المنطق التالي بتغيير قيمة x كل مرة للتبع ما يحصل في كل مرة:

  • x = -5
  • x = 0
  • x = 1
  • x = 5
x = -5

if x < 0:
    x = 0
    print('Set to zero')
elif x == 0:
    print('Zero')
elif x == 1:
    print('Single')
else:
    print('More')

print("Always:", x)
Set to zero
Always: 0

flowchart TD
    IF{{if x < 0}}
    IF -- False --> ELIF{{elif x == 0}}
    IF -- True --> S1[x = 0] --> S2["Set to zero"]
    ELIF -- False --> ELSE{{else}}
    ELIF -- True --> S3[x = 1] --> S4["Single"]
    ELSE --> S5["More"]

    Always[Always: x]
    S2 --> Always
    S4 --> Always
    S5 --> Always

    style IF fill:#82aeff, stroke:#333, stroke-width:2px;
    style ELIF fill:#82aeff, stroke:#333, stroke-width:2px;
    style ELSE fill:#82aeff, stroke:#333, stroke-width:2px;
    linkStyle 0,3 color:red;

وهذا تصوير لسير الأفعال لنفس الكود. ملاحظة: اضغط على الزر Prev أو Next للتنقل بين خطوات التنفيذ الفعلية:

لاحظ أن العبارة الأخيرة خارج كل اللَّبِنات الشرطية لذا يتم تنفيذها دائمًا.

3.4 جملة التعيين المشروط

تستطيع في بايثون أن تجعل جملة التعيين تأخذ قيمتها بحسب شرطٍ معين. مثلاً:

flowchart TD
    IF{{if age > 14}}
    IF -- False --> S2["status = 'child'"]
    IF -- True --> S1["status = 'adult'"]

    style IF fill:#82aeff, stroke:#333, stroke-width:2px;
    linkStyle 0 color:red;

age = 18
status = 'adult' if age > 14 else 'child'

print(status)
adult

وهي مكافئة للكود التالي:

age = 18

if age > 14:
  status = 'adult'
else:
  status = 'child'

print(status)
adult

ويحصل تسلسل التعيين المشروط بالصيغة التالية:

flowchart TD
  S1["score = 75"] --> IF1
  IF1{{if score >= 90}} -- True --> A["grade = 'A'"]
  IF1 -- False --> IF2{{if score >= 80}} -- True --> B["grade = 'B'"]
  IF2 -- False --> C["grade = 'C'"]
  
  style IF1 fill:#82aeff, stroke:#333, stroke-width:2px;
  style IF2 fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 2,4 color:red;

score = 75
grade = "A" if score >= 90 else "B" if score >= 80 else "C"
print(grade)
C

3.5 تضمين الجمل الشرطية

جمل الشرط المضمنة هي جمل if داخل جمل if أخرى. على سبيل المثال:

flowchart TD
  IF1{{if condition_1}}
  IF1 -- False --> ELSE1
  IF1 -- True --> IF2{{if condition_2}}
  IF2 -- False --> code_2
  IF2 -- True --> code_1
  ELSE1[code_3]

  style IF1 fill:#82aeff, stroke:#333, stroke-width:2px;
  style IF2 fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 0,2 color:red;

if <boolean expression>:
    if <boolean expression>:
        <code>
    else:
        <code>
else:
    <code>

ننبه مرة أخرى أن المحاذاة مهمة. يجب أن تكون جملة if الداخلية ذات محاذاة أكثر من جملة if الخارجية.

مثال

افترض أننا نريد تعيين درجة لطالب بناءً على نتيجته، وفقًا للجدول التالي:

Mark Grade
95-100 A+
90-94 A
85-89 B+
80-84 B
70-79 C
60-69 D
0-59 F
score = 95

if score >= 90:
  if score >= 95:
    print("Outstanding: A+")
  else:
    print("Excellent: A")

elif score >= 80:
  if score >= 85:
    print("Very Good: B+") 
  else:
    print("Good: B")

elif score >= 70:
  print("Okay: C")

elif score >= 60:
  print("Poor: D")

else:
  print("Failed: F")
Outstanding: A+

وهذا تصوير له:

3.6 التكرار

حلقة التكرار: هي الرجوع بالتنفيذ لجملة سابقة (غالبًا تكون سطرًا سابقًا) وذلك يحصل عددًا من المرات أو معلَّقًا بشرط.

فللتكرار طريقتان:

  • سرد متوالية (for): حيث يعيَّن متغير التكرار لكل عنصر في المتوالية، واحدًا تلوَ الآخر. (وسيأتي ذكره في باب المتسلسلات)
  • التكرار بشرط (while): حيث يستمر التكرار مادام الشرط متحققًا. (وهو موضوع هذا القسم)

التكرار بشرط

نبدأ بالتكرار بالطريقة غير المحددة، وهي جُملة تبدأ بالكلمة while على النحو التالي. كأنها if متكررة إلى حين تخلُّف الشرط (أن يصبح False). وإن لم يتحقق الشرط أصلاً فلا ينفذ الكود المضمَّن أصلا:

flowchart TD
  WHILE{{while condition}}
  WHILE -- False --> END
  WHILE -- True --> S1[code] --> WHILE
  
  style WHILE fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 0 color:red;

while <boolean expression>:
    <code>

مثال:

flowchart TD
  S0["i = 0"] --> WHILE{{"while i < 5"}}
  WHILE -- False --> S3["Done"]
  WHILE -- True --> S1["print(i)"] --> S2["i += 1"] --> WHILE

  style WHILE fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 0 color:red;

i = 0
while i < 5:
    print(i)
    i += 1
print('Done')
0
1
2
3
4
Done

لنقم بتتبع الخطوات عندما تكون i = 0:

  1. 0 < 5 تؤول True، فنطبع 0 ونزيد i لتصبح 1
  2. 1 < 5 تؤول True، فنطبع 1 ونزيد i لتصبح 2
  3. 2 < 5 تؤول True، فنطبع 2 ونزيد i لتصبح 3
  4. 3 < 5 تؤول True، فنطبع 3 ونزيد i لتصبح 4
  5. 4 < 5 تؤول True، فنطبع 4 ونزيد i لتصبح 5
  6. 5 < 5 تؤول False،فنخرج من الحلقة
  7. نطبع Done

وهذا تصوير لسير الأفعال لنفس الكود. ملاحظة: اضغط على الزر Prev أو Next للتنقل بين خطوات التنفيذ الفعلية:

لاحظ أن نسيان جملة الزيادة (i += 1) يجعل الشرط دائمًا صحيحًا، فيدور البرنامج في حلقة لا نهيائة ولا يخرج أبدًا. ويعتبر هذا خطأ برمجيًّا يتعذر على الكود التعامل معه بنفسه؛ بل يجب على المبرمج أن يكتشفه. وسيأتي الكلام عن أنواع الأخطاء في البرمجة في باب الأخطاء.

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

  • تستخدم كلمة break لإيقاف عملية التكرار كلها.
  • تستخدم كلمة continue للانتقال إلى الكرة التالية متخطيةً بقية الخطوات في الكرة الحالية.

أولاً نمثل لاستعمال جملة break على النحو التالي:

flowchart TD
  S0["i = 0"] --> WHILE{{"while i < 5"}}
  WHILE -- False --> Done["Done"]
  WHILE -- True --> IF1{{"if i == 3"}}
  IF1 -- break --> Done
  IF1 -- False --> S1["print(i)"] --> S2["i += 1"] --> WHILE

  style WHILE fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 1,4 color:red;

i = 0
while i < 5:
    if i == 3:
        break
    print(i)
    i += 1
print('Done')
0
1
2
Done

وهذا مثال لاستخدام عبارة continue للتخطي:

flowchart TD
  S0["i = 0"] --> WHILE{{"while i < 5"}}
  WHILE -- False --> Done["Done"]
  WHILE -- True --> IF1{{"if i == 3"}}
  IF1 -- continue -->  WHILE
  IF1 -- False --> S1["print(i)"] --> S2["i += 1"] --> WHILE

  style WHILE fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 1,4 color:red;

i = 0
while i < 5:
    if i == 3:
        i += 1
        continue
    print(i)
    i += 1
print('Done')
0
1
2
4
Done

أما جملة break المضمنة تحت طبقتين من التكرار فإن الذي يتوقف هو التكرار الداخلي فقط، ولا يتوقف الخارجي. وهذا مثال:

flowchart TD
  INIT1["i = 0"] --> WHILE1
  WHILE1{{"while i < 3"}}
    S3["i += 1"] --> WHILE1
    WHILE1 -- True --> INIT2["j = 0"] --> WHILE2
    WHILE2{{"while j < 3"}}
      WHILE2 -- True --> IF1{{"if i == 1"}} -- break --> WHILE1
      IF1 -- False --> S1["print(i, j)"] --> S2["j += 1"] --> WHILE2
    WHILE2 -- False --> S3

  style WHILE1 fill:#82aeff, stroke:#333, stroke-width:2px;
  style WHILE2 fill:#82aeff, stroke:#333, stroke-width:2px;
  linkStyle 5,8 color:red;

i = 0
while i < 3:
    j = 0
    while j < 3:
        if i == 1:
            break
        print(i, j)
        j += 1
    i += 1
print('Done')
0 0
0 1
0 2
2 0
2 1
2 2
Done