اذهب إلى المحتوى

بنى التحكم في لغة بايثون Python


عبد اللطيف ايمش

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

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

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

8-rain-flowchartمعرب-.jpg

مخطط تدفقي يخبرك ما تفعل إن كانت السماء تمطر

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

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

القيم المنطقية Boolean

يكون لأنواع البيانات التي تعلمناها سابقًا من سلاسل نصية وأرقام صحيحة وأرقام ذات فاصلة، عدد لا متناهي من القيم التي يمكن أن تأخذها. لكن للقيم المنطقية أو البوليانية تملك نوعين من القيم فقط: True و False (نقول عنها أنها قيم بوليانية Boolean نسبةً إلى العالم الرياضي جورج بولي)، وحين إدخالها في شيفرة بايثون فستلاحظ عدم وجود علامات الاقتباس التي تحيط بالسلاسل النصية، وتبدأ دومًا بالحرف الكبير T أو F وتكون بقية الكلمة بأحرف صغيرة.

أدخل ما يلي في الصدفة التفاعلية، لاحظ الأخطاء التي ستظهر لك في بعض التعليمات:

 >>> spam = True
   >>> spam
   True
 >>> true
   Traceback (most recent call last):
     File "<pyshell#2>", line 1, in <module>
       true
   NameError: name 'true' is not defined
 >>> True = 2 + 2
   SyntaxError: can't assign to keyword

وكأي قيمة أخرى، يمكن للقيم المنطقية أن تستعمل في التعابير البرمجية ويمكن أن تخزن في المتغيرات ➊، وإن لم تستعمل حالة الأحرف الصحيحة ➋ أو جربت استخدام True أو False لأسماء المتغيرات ➌ فستظهر لك رسالة خطأ.

عوامل المقارنة

تقارن عوامل المقارنة comparison operators بين قيمتين وتكون النتيجة هي قيمة منطقية بوليانية واحدة، الجدول الآتي يستعرض عوامل المقارنة:

العامل المعنى
== يساوي
!=‎ لا يساوي
< أصغر
> أكبر
<= أصغر أو يساوي
>=‎ أكبر أو يساوي

الجدول 2: عوامل المقارنة

تكون نتيجة استخدام هذه العوامل هي True أو False اعتمادًا على القيم التي تعطيها لها. لنجرب الآن بعض تلك العوامل ولنبدأ بالعاملين == و ‎!=‎:

>>> 42 == 42
True
>>> 42 == 99
False
>>> 2 != 3
True
>>> 2 != 2
False

وكما قد تتوقع، ستكون نتيجة عامل «يساوي» == هي True حينما تساوت القيمتان على يمينه ويساره، وعامل «لا يساوي» ستكون نتيجته True حينما تختلف القيمتان على يمينه ويساره. يمكننا استخدام العاملين == و ‎!= على أي نوع من أنواع البيانات:

  >>> 'hello' == 'hello'
   True
   >>> 'hello' == 'Hello'
   False
   >>> 'dog' != 'cat'
   True
   >>> True == True
   True
   >>> True != False
   True
   >>> 42 == 42.0
   True
 >>> 42 == '42'
   False

لاحظ أن القيم العددية سواءً كانت صحيحة int أو ذات فاصلة عائمة float لا تتساوي مع القيمة النصية. فالتعبير 42 == '42' ➊ نتيجته هي Flase لأن بايثون تعدّ الرقم 42 مختلفًا عن السلسلة النصية '42'.

أما المعاملات ‎> و ‎<‎ و <=‎ و ‎>= في لا تعمل إلا مع القيم العددية:

   >>> 42 < 100
   True
   >>> 42 > 100
   False
   >>> 42 < 42
   False
   >>> eggCount = 42
 >>> eggCount <= 42
   True
   >>> myAge = 29
 >>> myAge >= 10
   True

الفرق بين عامل = و ==

قد تلاحظ أن عامل المساواة == يحتوي على إشارتي يساوي، بينما عامل الإسناد فيه إشارة يساوي واحدة. ومن السهل الخلط بينهما بالنسبة إلى المبتدئين، لذا تذكر أن:

  • عامل المساواة == يتأكد إن كانت القيمتان عن يمينه ويساره متساويتين.
  • عامل الإسناد = يضع التي على اليمين في المتغير الذي على اليسار.

قد يساعدك في التذكر أن عامل المساواة == فيه حرفان، مثل معامل عدم المساواة ‎!=‎ تمامًا.

ستستخدم عوامل المقارنة كثيرًا لمقارنة قيمة أحد المتغيرات مع قيمة أخرى، كما في eggCount <= 42 ➊ و myAge >= 10 ➋، فلو كنتَ تعرف قيمة المتغير كيف ستكون قبل أن تشغل برنامجك فلا حاجة إلى المقارنة كلها، فليس من المنطقي أن تكتب 'dog' != 'cat' في شيفرتك بل تكتب True مباشرةً. سترى أمثلة كثيرة عن ذلك أثناء تعلمك لبنى التحكم.

العوامل المنطقية البوليانية

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

العوامل المنطقية الثنائية

يعمل العاملان and و or على قيمتين أو تعبيرين منطقيين، لهذا يسميان بالعوامل المنطقية الثنائية binary Boolean operators. ينتج العامل nad القيمة True إذا كانت كلا القيمتان المنطقيتان تساوي True، وإلا فالنتيجة هي False. أدخل التعابير البرمجية الآتية في الصدفة التفاعلية لترى أثر هذا العامل عمليًا:

>>> True and True
True
>>> True and False
False

جداول الحقيقة truth table هو جدول في الجبر المنطقي البولياني يوضح ناتج كل شكل من أشكال التعابير المنطقية. جدول الحقيقة الآتي يوضح ناتج كل عملية ممكنة مع العامل and:

التعبير النتيجة
True and True True
True and False False
False and True False
False and False False

الجدول 2: جدول الحقيقة للعامل and

وفي المقابل تكون نتيجة العامل or هي True إذا كان أحد القيمتين المنطقيتين يساوي True، أما إذا كانت كلتاهما Flase فنتيجة التعبير هي False:

>>> False or True
True
>>> False or False
False

يمكنك أن تعرف ناتج كل تعبير ممكن مع العامل or من جدول الحقيقة الخاص به.

التعبير النتيجة
True or True True
True or False True
False or True True
False or False False

الجدول 3: جدول الحقيقة للعامل or

العامل not

وعلى النقيض من العاملين and و or، يستخدم العامل not على قيمة أو تعبير منطقي واحد، ولهذا هو عامل أحادي unary operator. ما يفعله العامل not هو عكس القيمة المنطقية الحالية:

   >>> not True
   False
 >>> not not not not True
   True

وكما في نفي النفي في حديثنا العادي، يمكننا أن نجعل العامل not متشعبًا ➊، لكن لا يوجد سبب لفعل ذلك في البرامج العملية. الجدول الآتي هو جدول الحقيقة للعامل not:

التعبير النتيجة
not True False
not False True

الجدول 4: جدول الحقيقة للعامل not

المزج بين العوامل المنطقية وعوامل المقارنة

لما كانت نتيجة استخدام عوامل المقارنة هي قيمة منطقية، فيمكننا استخدامها في تعابير برمجية مع العوامل المنطقية.

تذكر أن العوامل المنطقية and و or و not هي عوامل منطقية لأنها تعمل على القيم المنطقية True و False؛ بينما التعابير التي تحتوي على عوامل مقارنة مثل ‎4 < 5 ليست قيمًا منطقية بحد ذاتها لكنها تعابير تُنتِج قيمًا منطقية.

جرب إدخال بعض التعابير المنطقية التي فيها عوامل مقارنة في الصدفة التفاعلية:

>>> (4 < 5) and (5 < 6)
True
>>> (4 < 5) and (9 < 6)
False
>>> (1 == 2) or (2 == 2)
True

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

يمكنك أن تتخيل أن عملية تقدير قيمة التعبير البرمجي ‎(4 < 5) and (5 < 6)‎ تشبه ما يلي:

9-boolean-and-comparison-operators.jpg

يمكنك أن تستعمل أكثر من عامل منطقي في تعبير واحد، بالإضافة إلى عوامل المقارنة:

>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2
True

سيكون للعوامل المنطقية ترتيب لتقدير القيمة كما في العوامل الرياضية، فبعد تقدير قيمة العمليات الحسابية وعمليات المقارنة، ستعمل بايثون على عوامل not أولًا، ثم عوامل and ثم عوامل or.

عناصر بنى التحكم

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

الشروط

تعدّ جميع التعابير المنطقية البوليانية التي رأيتها حتى الآن شروطًا conditions، فالشروط هي تعابير برمجية، لكنها تطلق في سياق الحديث عن بنى التحكم.

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

الكتل البرمجة

تجمَِع أسطر الشيفرات البرمجية في بايثون على شكل كتل blocks، ويمكنك أن تعرف متى تبدأ الكتلة ومتى تنتهي من المسافة البادئة indent للأسطر البرمجية.

هنالك ثلاث قواعد للكتل البرمجية في بايثون:

  • تبدأ الكتلة البرمجية حين زيادة المسافة البادئة.
  • يمكن أن تحتوي الكتل البرمجية على كتل أخرى.
  • تنتهي الكتل حينما تقل المسافة البادئة إلى الصفر أو إلى المسافة البادئة للكتلة الأب.

من الأسهل فهم الكتل البرمجية بالنظر إلى بعض الشيفرات التي لها مسافة بادئة، لذا لنعثر على الكتل البرمجية في البرنامج البسيط الآتي:

 name = Ahmed
  password = 'swordfish'
  if name == 'Ahmed':
     print('Hello, Ahmed’)
       if password == 'swordfish':
        ➋ print('Access granted.')
       else:
        ➌ print('Wrong password.')

تبدأ أول كتلة من الشيفرات ➊ في السطر print('Hello, Ahmed’)‎ وتضم إليها جميع الأسطر البرمجية التي تليها، وداخل هذه الكتلة هنالك كتلة أخرى ➋ التي تحتوي سطرًا واحدًا داخلها فيه print('Access granted.')‎، أما الكتلة الثالثة ➌ والأخيرة ففيها print('Wrong password.')‎.

تنفيذ البرنامج

تبدأ بايثون بتنفيذ مثالنا السابق hello.py من أول البرنامج حتى نهايته سطرًا بسطر، وعملية تنفيذ البرنامج (التي تسمى program execution أو اختصارًا execution) هي اصطلاح يشير إلى التعليمة البرمجية التي يجري تنفيذها حاليًا، فلو كانت شيفرة برنامجك مطبوعةً على ورقة وتشير بإصبعك إلى السطر الذي يجري تنفيذه حاليًا فتشير إصبعك هنا إلى خط سير تنفيذ البرنامج.

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

عبارات بنى التحكم

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

عبارة if

أكثر نوع شائع من بنى التحكم هو العبارة الشرطية if، ففيها ستُنفَّذ الكتلة البرمجية التي تلي if إذا كان الشرط محققًا أي True، وسيتخطاها البرنامج إن لم يكن الشرط محققًا أي False.

فإذا أردنا أن نقرأ عبارة if البرمجة باللغة العربية فسنقول «إذا كان الشرط محققًا، فنفذ الكتلة البرمجية الآتية».

تتألف عبارة if في بايثون مما يلي:

  • الكلمة المفتاحية if
  • الشرط، وهو التعبير الذي تكون نتيجته هي True أو False
  • نقطتان رأسيتان :
  • كتلة برمجية تلي ما سبق، تكون فيها الأسطر مسبوقة بمسافة بادئة

لنقل مثلًا أنك تريد كتابة شيفرة تتحقق إن كان اسم المستخدم هو Ahmed، وعلى فرض أننا قد أسندنا سابقًا قيمةً ما إلى المتغير name:

if name == 'Ahmed':
    print('Hi, Ahmed.')

تنتهي جميع عبارات بنى التحكم بنقطتين رأسيتين : متبوعة بكتلة برمجية، والكتلة البرمجية في مثالنا هي التي تحتوي على print('Hi, Ahmed.')‎. يوضح الشكل الآتي المخطط التدفقي للشيفرة السابقة:

10-if-flowchartمعرب.jpg

عبارات else

يمكن اختياريًا أن يأتي بعد كتلة if العبارة else، وتنفذ كتلة else في حال كان شرط عبارة if غير محقق False. أي بالعربية يمكننا أن نقول «إذا كان الشرط محققًا، فنفذ الكتلة البرمجية الآتية، وإلا فنفذ هذه الكتلة».

لا تملك عبارة else شرطًا، وتتألف else مما يلي:

  • الكلمة المفتاحية else
  • نقطتان رأسيتان :
  • كتلة برمجية تلي ما سبق، تكون فيها الأسطر مسبوقة بمسافة بادئة

وبالعودة إلى مثالنا السابق للترحيب بالمستخدم، فسنضيف العبارة else لطباعة تحية مختلفة لأي شخص ليس اسمه أحمد:

if name == 'Ahmed':
    print('Hi, Ahmed.')
else:
    print('Hello, stranger.')

11-else-flowchart-معرب.jpg

يبين المخطط التدفقي للبرنامج السابق

عبارات elif

تعرفنا سابقًا على عبارة if و else التي يجب أن تنفذ إحداهما، لكن ماذا لو كنّا نريد وجود أكثر من احتمال أو أكثر من شرط؟ تعمل العبارة elif كأنها «وإلا إذا كان كذا» else if، وتأتي بعد عبارة if أو elif أخرى.

توفر عبارة elif شرطًا بديلًا يمكن التحقق إن كان محققًا إن كانت الشروط التي تسبقه غير محققة.

تتألف عبارة elif في بايثون مما يلي:

  • الكلمة المفتاحية elif
  • الشرط، وهو التعبير الذي تكون نتيجته هي True أو False
  • نقطتان رأسيتان :
  • كتلة برمجية تلي ما سبق، تكون فيها الأسطر مسبوقة بمسافة بادئة

لنضف عبارة elif إلى برنامجنا الذي نتحقق فيه من اسم المستخدم:

if name == 'Ahmed':
    print('Hi, Ahmed.')
elif age < 12:
    print('You are not Ahmed, kiddo.')

سنتحقق هذه المرة من عمر المستخدم، فإن كان عمره أقل من 12 فسيقول له «أنت لست أحمد يا غلام!». يمكننا تمثيل البرنامج بمخطط التدفق الآتي:

12-elif-flowchart-معرب.jpg

مخطط التدفق للعبارة elif

ستفَّذ الكتلة البرمجية التي تلي elif إن كان عمر المستخدم age < 12 وكان الشرط name == 'Ahmed'‎ غير محقق False. لكن إن كان كلا الشرطين غير محقق فسيتجاوز البرنامج تنفيذ الكتلتين البرمجيتين، وليس من الضروري أي ينفذ أحد الكتل البرمجية، فقد تنفذ عبارة واحدة أو لا تنفذ أي عبارة.

بعد أن يتحقق شرط إحدى العبارات الشرطية فسيتجاوز البرنامج بقية عبارات elif كلها. فعلى سبيل المثال أنشِئ الملف vampire.py وضع فيه الشيفرة الآتية:

age = 3000
if name == Ahmed:
    print('Hi, Ahmed.')
elif age < 12:
    print('You are not Ahmed, kiddo.')
elif age > 2000:
    print('Unlike you, Ahmed is not an vampire.')
elif age > 100:
    print('You are not Ahmed, grannie.')

أضفنا هنا عبارتَي elif لبرنامج الترحيب بالمستخدم، وأضفنا جوابين مختلفين بناءً على العمر age. يظهر المخطط التدفقي الآتي سير عمل البرنامج:

13-multiple-elif-flowchart-معرب.jpg

المخطط التدفقي لعبارات elif متعددة في برنامج vampire.py.

لترتيب عبارات elif أهمية، ولتجربة أهمية ترتيبها فلنحاول إضافة علّة لبرنامجنا. تذكر أن البرنامج سيتخطى عبارات elif بعد تحقيق شرط إحداها، لذا إذا غيرنا ترتيب الشيفرة إلى ما يلي وحفظناه باسم vampire2.py:

   name = 'Abdullatif'
   age = 3000
   if name == 'Ahmed':
       print('Hi, Ahmed.')
   elif age < 12:
       print('You are not Ahmed, kiddo.')
 elif age > 100:
       print('You are not Ahmed, grannie.')
   elif age > 2000:
       print('Unlike you, Ahmed is not an undead, vampire.')

إذا جربنا البرنامج وكانت قيمة المتغير age تساوي 3000 مثلًا، فقد تتوقع أن برنامج سيطبع العبارة التي تقول أن عمره أكثر من 2000 سنة، لكن ولمّا كانت الشرط age > 100 محققًا True (وذلك لأن 3000 أكبر من 100 بالفعل) ➊، فستعرض عبارة الترحيب بالعجوز وستخطى تنفيذ البرنامج جميع عبارات elif الأخرى. لذا من المهم الانتباه إلى ترتيب عبارات elif.

المخطط التدفقي الآتي يظهر سير تنفيذ الشيفرة السابقة. لاحظ كيف جرى تبديل المعين الذي يحتوي على age > 100 و age > 2000.

14-bug-flowchart-معرب.jpg

المخطط التدفقي لتنفيذ برنامج vampire2.py، لاحظ أن الفرع الذي عليه إشارة × لا ينفذ أبدًا، لكنه إذا كان العمر age أكبر من 2000 فهو بكل تأكيد أكبر من 100 أيضًا.

يمكنك أن تضع عبارة else بعد آخر عبارة elif اختياريًا، وفي هذه الحالة سنضمن تنفيذ كتلة برمجية واحدة فقط لا غير، أي في حال كانت جميع شروط if و elif هي False فستنفَّذ كتلة else. لنعد كتابة مثال الترحيب بالمستخدم لنستعمل فيه if و elif و else:

name = 'Abdullatif'
age = 3000
if name == 'Ahmed':
    print('Hi, Ahmed.')
elif age < 12:
    print('You are not Ahmed, kiddo.')
else:
    print('You are neither Ahmed nor a little kid.')

يظهر الشكل الآتي المخطط التدفقي للمثال السابق، الذي سنسميه littleKid.py.

15-if-elif-else-flowchart-معرب.jpg

المخطط التدفقي لبرنامج liitleKid.py.

يمكننا وصف هذا النمط من بنى التحكم باللغة العربية: «إذا كان أول شرط محققًا فافعل كذا، وإلا إن كان الشرط الثاني محققًا فافعل كذا، وإلا فافعل كذا». من المهم أن تنتبه إلى ترتيب عبارات if وelif و else حين استخدامها لكي تتجنب العلل المنطقية في برامجك. وتذكر أن هنالك عبارة if وحيدة فقط لا غير، وأي عبارات elif يجب أن تأتي بعدها؛ وإذا أردت ضمان تنفيذ إحدى الكتل البرمجية فأنهِ بنى التحكم بعبارة else.

حلقة التكرار while

يمكنك أن تعيد تنفيذ إحدى الكتل البرمجية مرارًا وتكرارًا باستخدام عبارة while. وستُنفَّذ الشيفرة الموجودة في الكتلة التي تلي شرط while طالما كان الشرط محققًا True.

تتألف عبارة while في بايثون مما يلي:

  • الكلمة المفتاحية while
  • الشرط، وهو التعبير الذي تكون نتيجته هي True أو False
  • نقطتان رأسيتان :
  • كتلة برمجية تلي ما سبق، تكون فيها الأسطر مسبوقة بمسافة بادئة

يمكنك ملاحظة أن عبارة while شبيهة جدًا بعبارة if، والفرق بينهما هو في السلوك. فبعد الانتهاء من الكتلة التي تلي شرط if سيكمل البرنامج تنفيذ الشيفرة التي تليها، لكن في نهاية كتلة while فسيعود تنفيذ البرنامج إلى بداية عبارة while. نسمي عبارة while عادةً «حلقة التكرار while» أو «حلقة while».

للنظر إلى الفرق بين العبارة الشرطية if وحلقة while لهما نفس الشرط وتفعلان نفس الفعل في الكتلة التي تليهما. هذه هي الشيفرة التي نستعمل العبارة الشرطية if فيها:

spam = 0
if spam < 5:
    print('Hello, world.')
    spam = spam + 1

وهذه هي الشيفرة التي نستعمل الحلقة while فيها:

spam = 0
while spam < 5:
    print('Hello, world.')
    spam = spam + 1

تشبه هذه التعابير بعضها بعضًا، إذ تتحقق if و while من أن قيمة المتغير spam أصغر من 5، ثم تعرض رسالةً ترحيبيةً وتزيد قيمة المتغير spam بمقدار 1.

لكن حينما تشغل البرنامجين السابقين فستجد نتيجةً مختلفةً لكلٍ منهما، ففي البرنامج الذي فيه if سترى عبارة الترحيب مرة واحدة، بينما تكرر العبارة الترحيبية 5 مرات في برنامج while! لننظر إلى المخططين التدفقيين للبرنامجين السابقين في الشكلين المواليين لنفهم ما حدث:

16-if-while-flowchart.jpg

المخطط التدفقي للبرنامج الذي يحتوي على العبارة الشرطية if.

17-if-while-flowchart.jpg

المخطط التدفقي للبرنامج الذي يحتوي على العبارة الشرطية while.

تتحقق العبارة الشرطية if من الشرط وتطبع "Hello, World" مرةً واحدةً بعد تحقق الشرط، ثم ينتهي تنفيذ البرنامج. بينما البرنامج الذي فيه حلقة while فسينفذ 5 مرات لأن قيمة العدد spam تزيد مرة في نهاية كل حلقة تكرار، وهذا يعني أن الحلقة ستنفذ 5 مرات قبل أن يصبح الشرط spam < 5 غير محقق False.

ستجري عملية التحقق من شرط حلقة while في بداية كل دورة iteration، أي في بداية كل تنفيذ لحلقة while؛ وإذا كان الشرط محققًا True فستنفذ الكتلة، ثم بعد ذلك يعاد التحقق من الشرط مجددًا. لاحظ أنه إذا كان شرط حلقة while غير محقق False من أول مرة فلن تنفَّذ الحلقة أبدًا وسيتخطاها البرنامج.

حلقة while المزعجة

هذا برنامج بسيط يطلب منك باستمرار أن تدخل "your name" في سطر الأوامر حين سؤاله عن اسمك.

أنشِئ ملفًا جديدًا من File ثم New وأدخل الشيفرة الآتية واحفظها في الملف yourName.py:

 name = ''
 while name != 'your name':
       print('Please type your name.')
     name = input()
 print('Thank you!')

يضبط البرنامجُ المتغيرَ name إلى سلسلة نصية فارغة ➊، وبهذا يكون الشرط name != 'your name'‎ محققًا True وبالتالي سيبدأ تنفيذ حلقة while ➋.

سيسأل البرنامجُ المستخدمَ عن اسمه، ثم يأخذ المدخلات ويخزنها في المتغير name ➌، ولمّا كنّا قد وصلنا إلى نهاية حلقة التكرار فسنعود إلى بداية حلقة while ونتحقق من الشرط، وإن لم يكن الاسم name مساويًا إلى السلسلة النصية 'your name' فيسكون الشرط محققًا True وسيعاد تنفيذ حلقة while مجددًا.

لكن حينما يدخل المستخدم الكلمتين your name فسيصبح شرط حلقة while غير محقق False، وبالتالي بدلًا من إعادة تكرار الحلقة فسيكمل برنامجنا تنفيذ بقية البرنامج ➍.

المخطط التدفقي في الشكل الآتي يوضح آلية عمل البرنامج yourName.py:

18-yourname-flowchart.jpg

المخطط التدفقي للبرنامج yourName.py.

لنجرب الآن البرنامج، بالضغط على زر F5 لتشغيله، ثم كتابة أي عبارة سوى "your name" عدة مرات قبل أن نرضخ للضغط الذي يمارسه البرنامج تجاهنا ونكتب "your name".

Please type your name.
Abdullatif
Please type your name.
Hsoub
Please type your name.
%#@#%*(^&!!!
Please type your name.
your name
Thank you!

إذا لم ندخل "your name" أبدًا فلن يصبح شرط while غير محقق False وبالتالي سيستمر تنفيذ البرنامج إلى الأبد. وفي حالة برنامجنا كانت لدينا الدالة input() التي تمكننا من إكمال سير البرنامج حينما ندخل العبارة الصحيحة وبالتالي تتغير حالة الشرط، لكن قد لا يتغير الشرط في بعض البرامج مما يسبب مشكلة، لذا هنالك حاجة لتعلم طريقة للخروج من حلقة while.

العبارة break

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

أليس ذلك بسيطًا؟ لنكتب برنامجًا يشبه البرنامج السابق لكنه يستخدم العبارة break للخروج من حلقة التكرار، أدخِل الشيفرة الآتية واحفظها في ملف باسم yourName2.py:

 while True:
       print('Please type your name.')
     name = input()
     if name == 'your name':
         break
 print('Thank you!')

ننشِئ في أول سطر ➊ حلقة تكرار لا نهائية، وذلك بجعل شرط حلقة while محققًا دومًا True، وبالتأكيد إذا وضعنا True شرطًا لحلقة while فستكون قيمته هي True دومًا.

بعد أن يبدأ تنفيذ حلقة التكرار فلن يخرج البرنامج منها إلا إذا استخدام عبارة break، لاحظ أن حلقات التكرار اللانهائية التي لا ينتهي تنفيذها أبدًا هي علّة منطقية في البرامج.

وكما في المثال السابق، سيطلب البرنامج من المستخدم أن يدخل your name ➋، وسنتحقق إن كان المتغير name يساوي 'your name' باستخدام البنية الشرطية if ➌، وإذا كان الشرط محققًا True فستنفذ العبارة break ➍، وسينتقل التنفيذ إلى خارج الحلقة وستطبع رسالة الشكر ➎.

إذا لم يكن شرط العبارة if محققًا فهذا يؤدي إلى دورة جديدة لحلقة while، وسيتحقق البرنامج من شرط تنفيذ حلقة while ➊، ولمّا كان الشرط محققًا True دومًا، فستنفذ حلقة التكرار مجددًا وتسأل المستخدم أن يدخل your name.

المخطط التدفقي في الشكل التالي يوضح آلية عمل البرنامج yourName2.py:

19-yourname2-flowchart.jpg

المخطط التدفقي للبرنامج yourName2.py مع حلقة تكرار لا نهائية، لاحظ أن المسار × لا ينفذ منطقيًا أبدًا لأن شرط الحلقة هو True دومًا.

هل وقعت في حلقة تكرار لا نهائية؟

إذا شغلت تطبيقًا يحتوي على علة تؤدي إلى حلقة تكرار لا نهائية، فاضغط على Ctrl+C أو أعد تشغيل الصدفة من Shell ثم Restart Shell؛ مما يرسل إشارة KeyboardInterrupt إلى برنامجك تؤدي إلى إيقاف تشغيله مباشرةً. يمكنك التجربة بإنشاء برنامج بسيط باسم infiniteLoop.py:

while True:
    print('Hello, world!')

سيطبع البرنامج السابق العبارة Hello, World!‎ إلى اللانهاية لأن شرط حلقة while محقق True دومًا. قد تستفيد من استخدام الاختصار Ctrl+C لإنهاء تنفيذ البرامج حتى دون أن تكون عالقًا في حلقة تكرار لا نهائية.

عبارة continue

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

لنستخدم continue لكتابة برنامج يسأل عن اسم المستخدم وكلمة المرور، أدخل ما يلي في ملف جديد واحفظه باسم swordfish.py:

  while True:
      print('Who are you?')
      name = input()
     if name != 'Abdullatif':
         continue
       print('Hello, Abdullatif. What is the password? (It is a fish.)')
     password = input()
       if password == 'swordfish':
         break
 print('Access granted.')    

إذا أدخل المستخدم أي اسم باستثناء Abdullatif ➊ فستنقل العبارة continue ➋ التنفيذ إلى بداية حلقة التكرار، وحين إعادة التحقق من شرط الدخول إلى الحلقة فسيكون محققًا دومًا لأنه True.

بعد أن يتجاوز المستخدم الشرط الموجود في if فسنسأله عن كلمة المرور ➌، وإذا أدخل كلمة المرور swordfish فستنفذ عبارة break ➍ وبالتالي نخرج من حلقة التكرار كليًا وستطبع العبارة Access granted ➎، وإذا لم تكن كلمة المرور صحيحةً فسنصل إلى نهاية دورة حلقة التكرار ثم نعود إلى بدايتها ونتحقق من الشرط مجددًا الذي هو True دومًا… المخطط التدفقي في الشكل الآتي يوضح آلية عمل البرنامج swordfish.py:

20-swordfish-flowchart-معرب.jpg

المخطط التدفقي للبرنامج swordfish.py، لاحظ أن المسار × لا ينفذ منطقيًا أبدًا لأن شرط الحلقة هو True دومًا.

شغل البرنامج السابق وجرب بعض المدخلات، ولن يسألك البرنامج عن كلمة المرور حتى تدعي أنك Abdullatif، وسيطبع لك رسالة أن الوصول مسموح لك إن أدخلت كلمة المرور الصحيحة:

Who are you?
I'm fine, thanks. Who are you?
Who are you?
Abdullatif
Hello, Abdullatif. What is the password? (It is a fish.)
Mary
Who are you?
Abdullatif
Hello, Abdullatif. What is the password? (It is a fish.)
swordfish
Access granted.

القيم التي تكافئ True والقيم التي تكافئ False

ستعدّ الشروط في بايثون بعض القيم في أنواع البيانات المختلفة على أنها مكافئة للقيمة True وأخرى للقيمة False. فلو استخدمنا القيم 0 و 0.0 و '' (سلسلة نصية فارغة) في الشروط فستكافئ False، بينما ستكافئ أي قيمة أخرى True. ألقِ نظرةً هنا:

name = ''
 while not name:
    print('Enter your name:')
    name = input()
print('How many guests will you have?')
numOfGuests = int(input())
 if numOfGuests:
     print('Be sure to have enough room for all your guests.')
print('Done')

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

سيبدأ تنفيذ الحلقة بطلب إدخال الاسم وعدد الضيوف، وإذا كان عدد الضيوف numOfGuests ليس صفرًا 0 ➋ فسيكون الشرط محققًا True وسيطبع البرنامج تذكيرًا للمستخدم ➌.

كان بإمكاننا كتابة name != '' بدلًا من not name، و numOfGuests != 0 بدلًا من numOfGuests، لكن استخدام القيم التي تكافئ True أو False في شروط سيجعل مقروئية شيفرتك أفضل.

حلقات تكرار for والدالة range()‎

ستعمل حلقة while لطالما كان الشرط محققًا True (ومن هنا أتى اسمها while)، لكن ماذا لو أردنا تنفيذ كتلة من الشيفرات لعدد محدد من المرات؟ يمكننا فعل ذلك عبر حلقة التكرار for والدالة range().

ستبدو عبارة for كالآتي for i in range(5): وستتضمن ما يلي:

  • الكلمة المحجوزة for
  • اسم المتغير
  • الكلمة المحجوزة in
  • استدعاء للدالة range()‎ مع تمرير 3 أعداد صحيحة كحد أقصى إليها
  • نقطتان رأسيتان :
  • كتلة برمجية تلي ما سبق، تكون فيها الأسطر مسبوقة بمسافة بادئة

لننشئ برنامجًا ولنسمه fiveTimes.py الذي يساعدنا على معاينة حلقة for عمليًا:

print('My name is')
for i in range(5):
    print('Hani Five Times (' + str(i) + ')')

سترى أن الكتلة البرمجية لحلقة التكرار for تنفذ 5 مرات، وستكون قيمة المتغير i في أول مرة تعمل فيها الحلقة هو 0، وستطبع print()‎ العبارة Hani Five Times (0)‎ ثم بعد أن تنتهي بايثون من تنفيذ أول دورة في حلقة التكرار فسيعود التنفيذ إلى بداية الحلقة وستزيد قيمة المتغير i بمقدار 1، ولهذا سيؤدي استدعاء الدالة range(5)‎ إلى حدوث 5 تكرارات داخل حلقة for، إذ سيبدأ المتغير i من القيمة 0 ثم 1 ثم 2 ثم 3 ثم 4، وعمومًا ستزداد قيمة المتغير إلى أن تصل إلى الرقم الذي مررناه إلى الدالة range()‎ لكن دون تضمينه في النتائج.

الشكل الآتي يوضح المخطط التدفقي لهذا البرنامج:

21-fiveTimes-flowchart-معرب-.jpg

إذا شغلت البرنامج فسترى العبارة Hani Five Times متبوعةً بقيمة المتغير i في دورة حلقة for الحالية:

My name is
Hani Five Times (0)
Hani Five Times (1)
Hani Five Times (2)
Hani Five Times (3)
Hani Five Times (4)

ملاحظة: يمكنك استخدام break و continue داخل حلقات for أيضًا، وستؤدي العبارة continue إلى تخطي الدورة الحالية لحلقة التكرار والانتقال إلى الدورة الآتية، كما لو أن تنفيذ البرنامج وصل إلى نهاية دورة حلقة for الحالية ثم عاد إلى بداية الحلقة. يمكنك أن تستعمل العبارتين break و continue داخل حلقات while و for فقط، وإذا حاولت أن تجرب استخدامها في مكانٍ آخر فستعطيك بايثون رسالة خطأ.

لنأخذ قصة عالم الرياضيات الشهير كارل فريدريش غاوس (قد تعرفه باسم غاوس أو Gauss)، أراد مدرسه حينما كان صغيرًا أن يعطيه وظيفة صعبة وطلب منه جمع الأرقام من 0 إلى 100، وخطرت ببال الفتى غاوس طريقة ذكية للإجابة عن ذاك السؤال بثوانٍ معدودة، لكن لنكتب الآن برنامج بايثون يحسب لنا الناتج ويستعمل الحلقة for:

 total = 0
 for num in range(101):
     total = total + num
 print(total)

يفترض أن يكون الناتج 5,050. نضبط قيمة المتغير total إلى 0 ➊ في بداية البرنامج، ثم نبدأ حلقة for ➋ التي ننفذ فيها total = total + num ➌ مئة مرة، وبعد أن تنتهي دورات التكرار المئة فسنكون قد جمعنا الأعداد الصحيحة من 0 إلى 100 في المتغير total، ثم نطبق قيمة total إلى الشاشة ➍. سيعمل برنامجنا بأجزاء من الثانية ويخبرنا بالناتج النهائي.

(اكتشف غاوس حينما أعطاه المدرس هذه الوظيفة حلها بطريقة ذكية: هنالك خمسون زوجًا من الأرقام التي يكون مجموعها 101 مثل 1 + 100 و 2 + 99 و 3 + 98 وهلمَّ جرًا، حتى يصل إلى 50 + 51. ولمّا كان جداء 50 × 101 هو 5,050 فسيكون مجموع جميع الأرقام من 0 إلى 100 هو 5,050. يا له من فتى ذكي!)

كتابة حلقة while تكافئ حلقة for

يمكنك عمليًا أن تكتب حلقة while لتكافئ في عملها حلقة for، وستجد أن كتابة حلقات for مختصرة أكثر. لنعد كتابة المثال iveTimes.py ليستعمل الحلقة while:

print('My name is')
i = 0
while i < 5:
    print('Hani Five Times (' + str(i) + ')')
    i = i + 1

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

وسائط الدالة range()‎: البداية والنهاية والخطوة

يمكن أن تستدعى بعض الدوال مع عدّة وسائط arguments مفصولة بفاصلة، والدالة range()‎ هي إحداها.

يسمح لك تمرير وسائط للدالة range()‎ أن تغير سلوكها، فمثلًا يمكنك تحديد الرقم الذي يجب أن تبدأ منه الدالة range()‎:

for i in range(12, 16):
    print(i)

يمثِّل أول وسيط من أين يجب أن تبدأ حلقة for، ويمثل الوسيط الثاني أين يجب أن تتوقف (دون تضمين هذا الرقم):

12
13
14
15

يمكننا أيضًا استدعاء الدالة range()‎ مع ثلاثة وسائط، ويكون أول وسيطين هما البداية والنهاية، أما الوسيط الثالث فسيكون «الخطوة» step، الذي يشير إلى مقدار زيادة قيمة المتغير عند كل دورة:

for i in range(0, 10, 2):
    print(i)

أي أن استدعاء range(0, 10, 2)‎ سيعد من الصفر إلى الثمانية وبخطوة 2:

0
2
4
6
8

الدالة range()‎ مرنة ويمكنك استخدامها لتوليد أي سلسلة أرقام، فمثلًا يمكنك استخدام رقم سالب كوسيط لقيمة الخطوة مما يؤدي إلى العد عكسيًا تنازليًا:

for i in range(5, -1, -1):
    print(i)

ستنتج حلقة for السابقة الناتج الآتي:

5
4
3
2
1
0

نجد أن المجال range(5, -1, -1)‎ مع حلقة التكرار for سيؤدي إلى طباعة 5 أعداد تنازليًا من 5 إلى 0.

استيراد الوحدات

يمكن لجميع برامج بايثون أن تستدعي مجموعةً من الدوال الأساسية نسميها الدوال المضمنة في اللغة أو «الدوال المضمنة» built-in functions، والتي تتضمن الدوال التي تعرفت عليها سابقًا مثل print()‎ و input()‎ و len()‎.

تأتي بايثون أيضًا مع مجموعة من الوحدات الأساسية modules التي نسميها بالمكتبة القياسية standard library. تتألف كل وحدة من عدد برامج بايثون التي فيها مجموعة من الدوال التي يمكنك استخدامها في برامجك.

فمثلًا تحتوي الوحدة math على مجموعة من الدوال المتعلقة بالعمليات الرياضية، بينما تضم الوحدة random مجموعة من الدوال التي تجري عمليات على الأرقام المولدة عشوائيًا، وهكذا.

قبل أن نستخدم الدوال الموجودة في إحدى تلك الوحدات في برامجنا، يجب علينا أولًا استيراد تلك الوحدة باستخدام العبارة import. وتتألف عبارة import مما يلي:

  • الكلمة المحجوزة import
  • اسم الوحدة التي نريد استيرادها
  • واختياريًا أسماء وحدات أخرى نريد استيرادها على أن نفصل بينها بفاصلة

بعد أن تستورد إحدى الوحدات فيمكنك أن تستعمل جميع الدوال الرائعة الموجودة فيها، ولنضرب مثالًا الوحدة random التي تمنحنا وصولًا إلى الدالة random.randint()‎ حين استيرادها.

احفظ الشيفرة الآتية في ملف باسم printRandom.py:

import random
for i in range(5):
    print(random.randint(1, 10))

سيطبع البرنامج السابق ناتجًا يشبه الناتج الآتي حين تشغيله:

4
1
8
4
1

ستكون نتيجة استدعاء الدالة random.randint()‎ هي عدد صحيح عشوائي يقع بين العددين الذين مررتهما إلى الدالة كوسيطين. ولمّا كانت الدالة randint()‎ موجودةً في الوحدة random، فعليك أن تكتب الكلمة random أولًا قبل اسم الدالة لتخبر بايثون أنك تريد استخدام الدالة الموجودة في الوحدة random، ونفصل بين اسم الوحدة واسم الدالة بنقطة.

هذا مثال لعبارة استيراد تستورد أربع وحدات في آنٍ واحد:

import random, sys, os, math

يمكننا الآن استخدام أي دالة موجودة في الوحدات الأربع السابقة، وسنتعلم المزيد عن تلك الوحدات لاحقًا في هذه السلسلة.

عبارة from import

هنالك شكل بديل لعبارة import يتضمن الكلمة المحجوزة from، والتي نتبعها باسم الوحدة، ثم الكلمة المحجوزة import ثم نجمة *؛ فمثلًا from random import *‎.

وحين استيراد الوحدات بهذا الشكل فلا حاجة إلى وضع السابقة random.‎ قبل أسماء الدوال التي نريد استدعاءها؛ لكن في المقابل من الأفضل كتابة الاسم الكامل للدالة مع السابقة التي تشير إلى اسم الوحدة لزيادة مقروئية الشيفرة البرمجية.

إنهاء تنفيذ البرنامج حينما نشاء باستخدام الدالة sys.exit()‎

آخر مفهوم من مفاهيم بنى التحكم التي سنشرحها في هذا المقال هو آلية إنهاء تنفيذ البرنامج.

ينتهي تنفيذ البرامج دومًا حينما يصل التنفيذ إلى نهاية الملف، لكن يمكننا إنهاء تنفيذ البرنامج قبل آخر تعليمة برمجية باستخدام الدالة sys.exit()‎. ولمّا كانت هذه الدالة جزءًا من الوحدة sys فعلينا أن نستورد تلك الوحدة في بداية البرنامج قبل استخدامها.

افتح محرر الشيفرات واكتب ما يلي واحفظه exitExample.py:

import sys

while True:
    print('Type exit to exit.')
    response = input()
    if response == 'exit':
        sys.exit()
    print('You typed ' + response + '.')

شغل البرنامج السابق وستجد أنك دخلت في حلقة تكرار لا نهائية دون وجود عبارة break داخلها، والطريقة الوحيدة لكي ينتهي تنفيذ البرنامج هي الوصول إلى استدعاء الدالة sys.exit()‎، ولأن قيمة المتغير response تساوي ما يدخله المستخدم عبر input()‎، فإذا أدخلت exit فستتحقق العبارة الشرطية if وسينتهي تنفيذ البرنامج.

برنامج قصير: احزر الرقم

جميع الأمثلة السابقة كانت بسيطة جدًا لكنها مناسبة لاستيعاب المفاهيم البرمجية الأساسية، لكن لنبني الآن برنامجًا متكاملًا نوظِّف فيه المعلومات التي تعلمناها. سنكتب في هذا القسم لعبة «احزر الرقم»، والتي ستبدو كما يلي حين تشغيلها:

I am thinking of a number between 1 and 20.
Take a guess.
10
Your guess is too low.
Take a guess.
15
Your guess is too low.
Take a guess.
17
Your guess is too high.
Take a guess.
16
Good job! You guessed my number in 4 guesses!

افتح محرر الشيفرات وأدخل الشيفرة الآتية واحفظها في ملف باسم guessTheNumber.py:

# This is a guess the number game.
import random
secretNumber = random.randint(1, 20)
print('I am thinking of a number between 1 and 20.')

# Ask the player to guess 6 times.
for guessesTaken in range(1, 7):
    print('Take a guess.')
    guess = int(input())

    if guess < secretNumber:
        print('Your guess is too low.')
    elif guess > secretNumber:
        print('Your guess is too high.')
    else:
        break    # This condition is the correct guess!

if guess == secretNumber:
    print('Good job! You guessed my number in ' + str(guessesTaken) + '
guesses!')
else:
    print('Nope. The number I was thinking of was ' + str(secretNumber))

لنقسم البرنامج إلى أقسام صغيرة ونناقشها كلًا على حدة، بدءًا من أعلى الملف:

# This is a guess the number game.
import random
secretNumber = random.randint(1, 20)

يبدأ البرنامج بتعليق في أول سطر فيه يشرح ماذا يفعل البرنامج، ثم يستورد الوحدة random لكي نستطيع استخدام الدالة random.randint()‎ لتوليد رقم ليحزره اللاعب، وسنخزن الرقم العشوائي الناتج في المتغير secretNumber.

print('I am thinking of a number between 1 and 20.')

# Ask the player to guess 6 times.
for guessesTaken in range(1, 7):
    print('Take a guess.')
    guess = int(input())

سيخبر البرنامج اللاعب أنه قد «فكر» في رقم سري ويملك اللاعب ست محاولات ليحزره، وستكون الشيفرة التي تسمح للاعب بإدخال رقم والتحقق منه موجودة في حلقة for التي ستنفذ ست مرات كحد أقصى.

أول ما تفعله حلقة التكرار هو طلب كتابة تخمين للرقم السري عبر input()‎، ولأن الدالة input()‎ تعيد سلسلةً نصية وليس رقمًا صحيحًا فنمرر الناتج إلى الدالة int()‎، وثم يخزن الرقم الذي أدخله المستخدم في المتغير guess.

    if guess < secretNumber:
        print('Your guess is too low.')
    elif guess > secretNumber:
        print('Your guess is too high.')

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

    else:
        break    # This condition is the correct guess!

إذا لم يكن تخمين اللاعب أصغر ولا أكبر من الرقم السري فهذا يعني أنه يساويه، وفي هذه الحالة سيخرج البرنامج من حلقة for عبر break.

if guess == secretNumber:
    print('Good job! You guessed my number in ' + str(guessesTaken) + ' guesses!')
else:
    print('Nope. The number I was thinking of was ' + str(secretNumber))

بعد نهاية حلقة for سيتحقق البرنامج عبر عبارة if…else أن اللاعب قد خمَّن الرقم السري الصحيح، ويعرض رسالة مناسبة لكل حالة. تذكر أن الشيفرات التي تقع بعد حلقة التكرار ستنفذ بعد انتهاء تنفيذ حلقة التكرار سواءً بإكمالها 6 مرات وعدم تخمين اللاعب للرقم الصحيح، أو بالخروج منها عبر break حين تخمين الرقم السري الصحيح.

سيعرض برنامجنا رسالتين نصيتين فيهما متغير يحمل قيمةً عدديةً صحيحةً guessesTaken و secretNumber، ولأننا نحاول ضم عدد صحيح إلى سلسلة نصية فيجب أن نمرر الرقم إلى الدالة str()‎ أولًا، ثم نضم السلاسل النصية عبر العامل + لتمريرها إلى الدالة print()‎ لطباعتها على الشاشة.

برنامج قصير: حجرة ورقة مقص

لنستخدم المفاهيم البرمجية التي تعلمناها لإنشاء لعبة «حجرة ورقة مقص» الشهيرة. سيكون ناتج تشغيل اللعبة كما يلي:

ROCK, PAPER, SCISSORS
0 Wins, 0 Losses, 0 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
p
PAPER versus...
PAPER
It is a tie!
0 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
s
SCISSORS versus...
PAPER
You win!
1 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
q

افتح نافذةً جديدةً في محرر الشيفرات وأدخل الشيفرة الآتية واحفظها في ملف باسم rpsGame.py:

import random, sys

print('ROCK, PAPER, SCISSORS')

ستتبّع هذه المتغيرات عدد مرات الربح والخسارة والتعادل

wins = 0
losses = 0
ties = 0

while True: # دورة اللعبة
    print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
    while True: # مدخلات المستخدم
        print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
        playerMove = input()
        if playerMove == 'q':
            sys.exit() # الخروج من البرنامج
        if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
            break # الخروج من حلقة مدخلات المستخدم
        print('Type one of r, p, s, or q.')

    # عرض ما اختاره اللاعب
    if playerMove == 'r':
        print('ROCK versus...')
    elif playerMove == 'p':
        print('PAPER versus...')
    elif playerMove == 's':
        print('SCISSORS versus...')

    # عرض ما اختاره الحاسوب
    randomNumber = random.randint(1, 3)
    if randomNumber == 1:
        computerMove = 'r'
        print('ROCK')
    elif randomNumber == 2:
        computerMove = 'p'
        print('PAPER')
    elif randomNumber == 3:
        computerMove = 's'
        print('SCISSORS')

    # عرض النتيجة الربح/الخسارة/التعادل
    if playerMove == computerMove:
        print('It is a tie!')
        ties = ties + 1
    elif playerMove == 'r' and computerMove == 's':
        print('You win!')
        wins = wins + 1
    elif playerMove == 'p' and computerMove == 'r':
        print('You win!')
        wins = wins + 1
    elif playerMove == 's' and computerMove == 'p':
        print('You win!')
        wins = wins + 1
    elif playerMove == 'r' and computerMove == 'p':
        print('You lose!')
        losses = losses + 1
    elif playerMove == 'p' and computerMove == 's':
        print('You lose!')
        losses = losses + 1
    elif playerMove == 's' and computerMove == 'r':
        print('You lose!')
        losses = losses + 1

لنمعن النظر إلى الشيفرة من بدايتها:

import random, sys

print('ROCK, PAPER, SCISSORS')

# ستتبّع هذه المتغيرات عدد مرات الربح والخسارة والتعادل
wins = 0
losses = 0
ties = 0

سنستورد في البداية الوحدتين random و sys لكي نستطيع استدعاء الدالتين random.randint()‎ و sys.exit()‎، ثم سنهيئ ثلاثة متغيرات لكي نتتبع عدد مرات ربح أو خسارة أو تعادل اللاعب.

while True: # دورة اللعبة
    print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
    while True: # مدخلات المستخدم
        print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
        playerMove = input()
        if playerMove == 'q':
            sys.exit() # الخروج من البرنامج
        if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
            break # الخروج من حلقة مدخلات المستخدم
        print('Type one of r, p, s, or q.')

يستعمل برنامجنا حلقة while داخل حلقة while، أول حلقة هي حلقة اللعبة الأساسية، وتمثل دورًا من لعبة «حجرة ورقة مقص» في كل مرة تنفذ فيها تلك الحلقة. أما حلقة التكرار الثانية فهي تطلب من اللاعب مدخلات، وستبقى تعمل حتى يدخل المستخدم r أو p أو s أو q التي ترمز إلى حجرة rock وورقة paper ومقص scissors على التوالي وبالترتيب، أما q فتعني أن اللاعب يريد إنهاء اللعبة quit، وفي تلك الحالة ستستدعى الدالة sys.exit()‎ وسنتهي تنفيذ البرنامج.

إذا أدخل المستخدم r أو p أو s فسنخرج من حلقة التكرار الثانية عبر break، وإلا فسيعود التنفيذ إلى بداية حلقة التكرار ويذكر البرنامج اللاعب أن عليه إدخال r أو p أو s أو q.

    # عرض ما اختاره اللاعب
    if playerMove == 'r':
        print('ROCK versus...')
    elif playerMove == 'p':
        print('PAPER versus...')
    elif playerMove == 's':
        print('SCISSORS versus...')

يظهر البرنامج هنا ما اختاره اللاعب.

    # عرض ما اختاره الحاسوب
    randomNumber = random.randint(1, 3)
    if randomNumber == 1:
        computerMove = 'r'
        print('ROCK')
    elif randomNumber == 2:
        computerMove = 'p'
        print('PAPER')
    elif randomNumber == 3:
        computerMove = 's'
        print('SCISSORS')

وهنا يظهر ما اختاره الحاسوب عشوائيًا.

ولمّا كانت الدالة random.randint()‎ تعيد رقمًا عشوائيًا، فسنحتاج إلى تحويل الرقم الصحيح المخزن في المتغير randomNumber إلى حجرة أو ورقة أو مقص عبر البنية الشرطية if و elif، وبالتالي سيخزن البرنامج ما اختاره الحاسوب في المتغير computerMove ثم يطبع رسالة نصية فيها الحركة المختارة.

    # عرض النتيجة الربح/الخسارة/التعادل
    if playerMove == computerMove:
        print('It is a tie!')
        ties = ties + 1
    elif playerMove == 'r' and computerMove == 's':
        print('You win!')
        wins = wins + 1
    elif playerMove == 'p' and computerMove == 'r':
        print('You win!')
        wins = wins + 1
    elif playerMove == 's' and computerMove == 'p':
        print('You win!')
        wins = wins + 1
    elif playerMove == 'r' and computerMove == 'p':
        print('You lose!')
        losses = losses + 1
    elif playerMove == 'p' and computerMove == 's':
        print('You lose!')
        losses = losses + 1
    elif playerMove == 's' and computerMove == 'r':
        print('You lose!')
        losses = losses + 1

وفي النهاية، سيوازن البرنامج بين السلسلتين النصيتين الموجودتين في المتغيرين playerMove و computerMove ويظهر الناتج على الشاشة، وسيزيد قيمة أحد المتغيرات wins أو losses أو ties بما يناسب الحالة. بعد أن يصل تنفيذ البرنامج إلى النهاية فسيعود إلى بداية حلقة التكرار الرئيسية ونلعب دورًا جديدًا من اللعبة.

الخلاصة

بعد أن تعلمنا كيفية كتابة شروط أو عبارات تكون نتيجتها True أو False، أصبح بإمكاننا كتابة برامج تستطيع اتخاذ قرارات تحدد ما هي الشيفرات التي سينفذها البرنامج وأيها سيتخطاها.

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

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

والآن هل تستطيع تجربة الآتي؟

كتابة شيفرة تطبع Hello إذا كانت القيمة 1 مخزنة في المتغير spam، و World إذا كانت القيمة 2 في المتغير spam، و Greetings فيما عدا ذلك. كتابة برنامج قصير يطبع الأرقام من 1 إلى 10 باستخدام حلقة for. ثم اكتب برنامجًا مكافئًا له يطبع الأرقام من 1 إلى 10 باستخدام حلقة while.

ترجمة -وبتصرف- للفصل Flow Control من كتاب Automate the boring stuff with Python لصاحبه Al Sweigart.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...