مصطلحات البرمجة
أهداف التعلّم
بعد إتمام هذا القسم
- ستصبح على معرفة ببعض المصطلحات الجوهرية في البرمجة
- ستعرف الفرق بين الجملة والتعبير
- ستتمكّن من معرفة نوع بيانات تعبير مُقيَّم
- ستتعلّم استخدام أساليب تنقيح الأخطاء للعثور على الأخطاء في شيفرتك
في الجزء الأول من هذه الدورة لم نُولِ المصطلحات اهتمامًا كبيرًا، فلننظر الآن إلى بعض المفاهيم الجوهرية في البرمجة.
الجملة
جملة (statement) هي جزء من البرنامج ينفّذ شيئًا ما. وكثيرًا ما تشير — لكن ليس دائمًا — إلى أمر واحد.
على سبيل المثال، print("Hi!") جملة تطبع سطرًا من النص. وبالمثل، number = 2 جملة تُسند قيمة إلى متغيّر.
قد تكون الجملة أعقد أيضًا. إذ قد تحتوي على جمل أخرى. تمتد الجملة التالية على ثلاثة أسطر:
if name == "Anna":
print("Hi!")
number = 2
في الحالة أعلاه توجد جملتان (جملة طباعة وجملة إسناد) داخل جملة شرطية.
الكتلة
كتلة (block) مجموعة من جمل متتالية تقع على المستوى نفسه في بنية البرنامج. فمثلًا، تحتوي كتلة الجملة الشرطية على الجمل التي تُنفَّذ فقط إذا كان الشرط صحيحًا.
if age > 17:
# beginning of the conditional block
print("You are of age!")
age = age + 1
print("You are now one year older...")
# end of the conditional block
print("This here belongs to another block")
في Python تُعبَّر الكتل بمسافة بادئة واحدة متساوية لكل شيفرة داخل الكتلة.
ملاحظة: يجب أن تكون الكتلة الرئيسية لبرنامج Python دائمًا في أقصى يسار الملف، دون مسافة بادئة:
# this program will not work because it is not written at the leftmost egde of the file
print("hello world")
print("this program is not very good...")
التعبير
تعبير (expression) هو جزء من الشيفرة ينتج عنُه نوع بيانات معيَّن. فعند تنفيذ البرنامج يُقيَّم التعبير بحيث تصبح له قيمة يمكن استخدامها بعد ذلك في البرنامج.
إليك بعض أمثلة التعبيرات:
| التعبير | القيمة | النوع | نوع بيانات Python |
|---|---|---|---|
2 + 4 + 3 |
9 |
عدد صحيح | int |
"abc" + "de" |
"abcde" |
سلسلة نصية | str |
11 / 2 |
5.5 |
عدد عشري | float |
2 * 5 > 9 |
True |
قيمة منطقية | bool |
ولأن جميع التعبيرات لها نوع، يمكن إسنادها إلى متغيّرات:
# the variable x is assigned the value of the expression 1 + 2
x = 1 + 2
ويمكن تجميع التعبيرات البسيطة معًا لتشكيل تعبيرات أعقد، مثلًا باستخدام عمليات حسابية:
# the variable y is assigned the value of the expression '3 times x plus x squared'
y = 3 * x + x**2
الدالة
دالة (function) تنفّذ وظيفة ما. ويمكن للدوال أيضًا أن تأخذ واحدًا أو أكثر من الوسائط (arguments)، وهي بيانات يمكن تغذية الدالة بها ومعالجتها. وأحيانًا تُسمّى الوسائط أيضًا معاملات (parameters). وهناك تمييز تقني بين الوسيط والمعامل، لكن الكلمتين تُستخدمان غالبًا بالتبادل. ويكفي الآن أن تتذكّر أن المصطلحين يشيران إلى فكرة البيانات المُمرَّرة إلى الدالة.
وتُنفَّذ الدالة عندما تُستدعى، أي عندما تُذكر الدالة (ووسائطها إن وُجدت) في الشيفرة. تستدعي الجملة التالية الدالة print بالوسيط "this is an argument":
print("this is an argument")
ودالة أخرى استخدمتها كثيرًا من قبل هي الدالة input، وهي تطلب مُدخَلًا من المستخدم. والوسيط لهذه الدالة هو الرسالة التي تُعرض على المستخدم:
name = input("Please type in your name: ")
وفي هذه الحالة تُعيد الدالة أيضًا قيمة. فبعد تنفيذ الدالة يُستبدل قسم الشيفرة الذي استُدعيت فيه بالقيمة التي تعيدها؛ فهو تعبير آخر أصبح الآن مُقيَّمًا. وتُعيد الدالة input قيمة سلسلة نصية تحتوي على كل ما كتبه المستخدم عند المُطالبة. وغالبًا ما تُخزَّن القيمة التي تعيدها الدالة في متغيّر ليُستخدم لاحقًا في البرنامج.
نوع البيانات
نوع البيانات (data type) يشير إلى خصائص أي قيمة موجودة في البرنامج. في الشيفرة التالية يكون نوع بيانات المتغيّر name هو سلسلة نصية أي str، ونوع بيانات المتغيّر result هو عدد صحيح أي int:
name = "Anna"
result = 100
يمكنك استخدام الدالة type لمعرفة نوع بيانات أي تعبير. وإليك مثالًا على استخدامها:
print(type("Anna"))
print(type(100))
<class 'str'>
<class 'int'>
الصياغة
تمامًا كما في اللغات الطبيعية، تحدّد الصياغة (syntax) لغة البرمجة كيف ينبغي أن تُكتب شيفرة البرنامج. ولكل لغة برمجة صياغتها الخاصة.
وتحدّد صياغة Python، ضمن أمور أخرى، أن السطر الأول لجملة if ينبغي أن ينتهي بحرف النقطتين، وأن كتلة الجملة ينبغي أن تكون بمسافة بادئة:
if name == "Anna":
print("Hi!")
وإذا لم تُتبَع القواعد النحوية للغة البرمجة، فسيحدث خطأ:
if name == "Anna"
print("Hi!")
File "test.py", line 1
if name == "Anna"
^
SyntaxError: invalid syntax
تنقيح الأخطاء
إذا كانت صياغة البرنامج صحيحة لكن البرنامج لا يعمل كما هو مقصود، فهناك خلل (bug) في البرنامج.
تظهر الأخطاء بطرق مختلفة. فبعض الأخطاء يسبب خطأ أثناء التنفيذ. فمثلًا، البرنامج التالي
x = 10
y = 0
result = x / y
print(f"{x} divided by {y} is {result}")
يسبب هذا الخطأ:
ZeroDivisionError: integer division or modulo by zero on line 3
المشكلة هنا رياضية بطبعها: القسمة على صفر غير مسموح بها، وهذا يوقف تنفيذ البرنامج.
عادةً ما تكون الأخطاء أثناء التنفيذ سهلة الإصلاح إلى حدٍّ كبير، لأن رسالة الخطأ تذكر سطر الشيفرة الذي سبّب الخطأ. وبالطبع قد يكون السبب الحقيقي للخلل في مكان مختلف تمامًا عن سطر الشيفرة الذي سبّب الخطأ.
أحيانًا يكشف خللٌ في البرنامج أن النتيجة التي تنتجها الشيفرة خاطئة. وقد يكون اكتشاف هذا النوع من الأخطاء وتحديد موضعه تحديًا. وفي تمارين البرمجة في هذه الدورة يكون الغرض من الاختبارات غالبًا هو كشف أخطاء من هذا النوع. وقبل إصلاح أي خلل يجب أولًا تحديد سببه.
تسمّي مصطلحات البرمجة عملية اكتشاف أسباب الأخطاء تنقيح الأخطاء (debugging). وهي مهارة بالغة الأهمية في صندوق أدوات أي مبرمج. وغالبًا ما يقضي المبرمجون المحترفون وقتًا أطول في تنقيح الأخطاء مما يقضونه في كتابة شيفرة جديدة.
إحدى الطرق البسيطة والفعّالة لتنقيح البرنامج هي إضافة جمل طباعة للتشخيص إلى شيفرتك. فتحقّق من نتائج شيفرتك بأوامر print يوفّر تأكيدًا سريعًا بأن الشيفرة تفعل ما تريده فعلًا.
إليك محاولة لحل أحد تمارين القسم السابق:
hourly_wage = float(input("Hourly wage: "))
hours = int(input("Hours worked: "))
day = input("Day of the week: ")
daily_wages = hourly_wage * hours
if day == "sunday":
daily_wages * 2
print(f"Daily wages: {daily_wages} euros")
البرنامج لا يعمل تمامًا كما ينبغي. يؤدي تشغيل الاختبارات إلى طباعة ما يلي:
FAIL: PythonEditorTest: test_sunday_1
With input 20.0,6,Sunday correct wage 240.0 is not found in output Daily wages: 120.0 euros
وعند تنقيح تمارين هذه الدورة، تكون الخطوة الأولى غالبًا هي التحقق من كيفية سلوك البرنامج مع المُدخَل المحدَّد في الاختبار الذي فشل. وبالفعل فإن النتيجة ليست كما كان متوقَّعًا:
Daily wages: 120.0 euros
يشير تنقيح الأخطاء عادةً إلى تشغيل البرنامج عدة مرات. فقد يكون من المفيد تثبيت المُدخَل المُعطِّل مؤقتًا، أي تثبيته بشكل صريح (hard-code)، بدلًا من طلب مُدخَل من المستخدم في كل مرة. في هذه الحالة قد يبدو التثبيت الصريح على النحو التالي:
# hourly_wage = float(input("Hourly wage: "))
# hours = int(input("Hours worked: "))
# day = input("Day of the week: ")
hourly_wage = 20.0
hours = 6
day = "Sunday"
daily_wages = hourly_wage * hours
if day == "sunday":
daily_wages * 2
print(f"Daily wages: {daily_wages} euros")
ويمكن أن تكون الخطوة التالية إضافة جمل طباعة للتشخيص. فالجزء المُعطِّل من الشيفرة موجود في القسم المتعلق بأيام الأحد، فلنضف أوامر print قبل السطر الذي ينبغي أن يضاعف الأجر اليومي في أيام الأحد وبعده:
# ...
daily_wages = hourly_wage * hours
if day == "sunday":
print("wages before:", daily_wages)
daily_wages * 2
print("wages after doubling:", daily_wages)
print(f"Daily wages: {daily_wages} euros")
وتشغيل الشيفرة الآن لا يكشف شيئًا — جمل طباعة التشخيص لا تُطبع إطلاقًا. يبدو أن محتويات كتلة if لا تُنفَّذ أبدًا، لذا لا بد أن هناك مشكلة في الجملة الشرطية. لنجرّب طباعة قيمة التعبير المنطقي:
# ...
daily_wages = hourly_wage * hours
print("condition:", day == "sunday")
if day == "sunday":
print("wages before:", daily_wages)
daily_wages * 2
print("wages after doubling:", daily_wages)
print(f"Daily wages: {daily_wages} euros")
وبالفعل، القيمة هي False، لذا لا تُنفَّذ محتويات كتلة if أبدًا:
condition: False
Daily wages: 120.0 euros
وإذن فلا بد أن المشكلة تكمن في شرط جملة if. فكما في حالات كثيرة في البرمجة، يهمّ حالة الأحرف في المقارنات أيضًا. لاحظ كيف أن كلمة "sunday" في التعبير المنطقي لم تُكتب بحرف كبير، بينما كانت في المُدخَل كذلك. فلنُصلح ذلك (في أمر print وفي جملة if معًا):
# ...
daily_wages = hourly_wage * hours
print("condition:", day == "Sunday")
if day == "Sunday":
print("wages before:", daily_wages)
daily_wages * 2
print("wages after doubling:", daily_wages)
print(f"Daily wages: {daily_wages} euros")
وتشغيل هذا يطبع ما يلي:
condition: True
wages before: 120
wages after doubling: 120
Daily wages: 120.0 euros
يبدو أن القيمة المخزّنة في daily_wages صحيحة في البداية: hourly_wage = 20.0 وhours = 6، و20.0 * 6 = 120.0. غير أن الأمر المفترض أن يضاعف الرقم لا يفعل ذلك، لذا لا بد أن هناك مشكلة في الأمر. وبالفعل فإن الأمر
daily_wages * 2
يقوم بمضاعفة القيمة، لكنه لا يخزّن القيمة الجديدة في أي مكان. لنغيّره بحيث يخزّن القيمة الجديدة أيضًا:
daily_wages *= 2
وتشغيل البرنامج من جديد يكشف أن المطبوع في النهاية صار صحيحًا أيضًا:
condition: True
wages before: 120
wages after doubling: 240
Daily wages: 240.0 euros
وحين يُصلَح البرنامج، تذكّر أن تُزيل جميع جمل طباعة التشخيص والشيفرة الأخرى التي أُضيفت لأغراض التنقيح.
كان هذا المثال بسيطًا إلى حدٍّ كبير، وفي برنامج قصير كهذا يمكن ربما استنتاج الأخطاء بقراءة الشيفرة قراءة متأنّية فحسب. لكن استخدام جمل طباعة التشخيص غالبًا ما يكون طريقة سريعة للاستئناس بمكان المشكلة المحتمل. ويمكن استخدام جمل الطباعة لتحديد أجزاء البرنامج التي تبدو عاملة على نحو صحيح، وبذلك وبذلك تتركّز جهود تتبّع الأخطاء على أقسام الشيفرة الأرجح أنها المسبِّبة للخلل.
جمل طباعة التشخيص ليست سوى أداة واحدة من أدوات تنقيح البرامج. وسنعود إلى هذا الموضوع لاحقًا في هذه الدورة. وينبغي أن تعتاد الآن على استخدام جمل طباعة التشخيص للبحث عن الأخطاء في شيفرتك. فالمتخصصون في البرمجة لا يستطيعون الاستغناء عنها، لذا فهي أداة مفيدة جدًا للمبتدئين أيضًا.
تمرين تفاعلي: إصلاح الصياغة
يحتوي البرنامج التالي على عدة أخطاء صياغية. يُرجى إصلاح البرنامج بحيث تصير الصياغة سليمة ويعمل البرنامج وفق ما تحدّده الأمثلة التالية.
number = input("Please type in a number: ")
if number>100
print("The number was greater than one hundred")
number - 100
print("Now its value has decreased by one hundred)
print("Its value is now"+ number)
print(number + " must be my lucky number!")
print("Have a nice day!)
Please type in a number: **13**
13 must be my lucky number!
Have a nice day!
Please type in a number: **101**
The number was greater than one hundred
Now its value has decreased by one hundred
Its value is now 1
1 must be my lucky number!
Have a nice day!
ملاحظة: هذا التمرين يتطلب بيئة TMC التفاعلية ولا يعمل في النسخة الثابتة.
تمرين تفاعلي: عدد المحارف
يمكن استخدام الدالة
lenلمعرفة طول سلسلة نصية، ضمن أمور أخرى. وتُعيد الدالة عدد المحارف في السلسلة النصية.
إليك بعض الأمثلة على كيفية عمل ذلك:
word = "abcd"
print(len(word))
print(len("hi there"))
word2 = "howdydoody"
length = len(word2)
print(length)
empty_string = ""
length = len(empty_string)
print(length)
4
8
10
0
يُرجى كتابة برنامج يطلب من المستخدم كلمة ثم يطبع عدد محارفها، إن كان قد أدخل أكثر من محرف واحد.
أمثلة على السلوك المتوقَّع:
Please type in a word: **hey**
There are 3 letters in the word hey
Thank you!
Please type in a word: **banana**
There are 6 letters in the word banana
Thank you!
Please type in a word: **b**
Thank you!
ملاحظة: هذا التمرين يتطلب بيئة TMC التفاعلية ولا يعمل في النسخة الثابتة.
تمرين تفاعلي: تحويل الأنواع
عند البرمجة في Python، غالبًا ما نحتاج إلى تغيير نوع بيانات قيمة ما. فمثلًا، يمكن تحويل عدد عشري إلى عدد صحيح بالدالة
int:
temperature = float(input("Please type in a temperature: "))
print("The temperature is", temperature)
print("...and rounded down it is", int(temperature))
Please type in a temperature: **5.15**
The temperature is 5.15
...and rounded down it is 5
لاحظ أن الدالة تُقرِّب إلى الأسفل دائمًا، ولا تتبع قواعد التقريب في الرياضيات. وهذه مثال على دالة أرضية (floor function).
Please type in a temperature: **8.99**
The temperature is 8.99
...and rounded down it is 8
يُرجى كتابة برنامج يطلب من المستخدم عددًا عشريًا ثم يطبع الجزء الصحيح والجزء العشري على حدة. استخدم دالة int في Python.
يمكنك افتراض أن الرقم الذي يعطيه المستخدم يكون دائمًا أكبر من الصفر.
مثال على السلوك المتوقَّع:
Please type in a number: **1.34**
Integer part: 1
Decimal part: 0.34
ملاحظة: هذا التمرين يتطلب بيئة TMC التفاعلية ولا يعمل في النسخة الثابتة.