الدوال البرمجية
(Functions)
تعرَّفْتُ سابقًا أنَّ الدالَّة البرمجية Function مقطع برمجي له اسم يؤدّي وظيفة ما، ويُمكِن استدعاؤه باستخدام اسمه؛ فكيف تُستخدَم الدوالُّ البرمجية في لغة البرمجة بايثون ) Python (؟ وما أهميتها في تصميم البرامج وتسهيل قراءتها والتعامل معها؟
الدوالُّ البرمجية
إذا افترضْتُ أنَّ الأمر print لم يكن موجودًا في لغة البرمجة بايثون Python وأنَّه يتعيَّن عليَّ
دائمًا طباعة أيِّ شيء على الشاشة بكتابة كامل الكود البرمجي الذي يقوم بالتعامل مع نظام التشغيل وأنواع البيانات المختلفة من أجل إظهارها على الشاشة بالشكل الصحيح، فإنَّ ذلك سيكون مُرهِقًا لي بلا شكٍّ، ويجعل قراءة البرنامج عسيرة. ولهذا، فإنَّ لغة البرمجة بايثون Python وفَّرت عليْنا هذا الجُهْد والعناء بتقديمها مقاطع برمجية جاهزة مُدقَّقة وخالية من الأخطاء، بحيث يُمكِننا استدعاؤها بكل سهولة عن طريق اسمها، واستخدامها في برامجنا من دون حاجة إلى كتابة الأوامر دائمًا.
الدوالُّ البرمجية الجاهزة
تُوفِّر لغة البرمجة بايثون Python عددًا كبيرًا من الدوالِّ البرمجية الجاهزة، وقد استخدمنا العديد
منها في أمثلة سابقة ورد ذكرها في هذه الوحدة. ونظرًا إلى كثرة هذه الدوالِّ، فإنَّه يصعب على
المُبرمِجين تذكُّرها جميعًا، أو حفظ كيفية استخدامها. ولهذا يُعَدُّ الموقع الإلكتروني للغة البرمجة
بايثون Python الملاذ والصديق لكل مُبرمِج مُحترِف؛ إذ يُواظِب كلٌّ منهم على زيارته باستمرار؛
للبحث عن دالَّة لوظيفة ما، أو تذكُّر كيف تعمل إحدى الدوالِّ.
الوحدات البرمجية Modules
تم تنظيم عدد الدوالِّ الكبير في لغة البرمجة بايثون Python عن طريق جمعها في وحدات
modules تحتوي الوحدة الواحدة منها على دوالَّ برمجية تشترك معًا في الغرض والاستخدام.
فمثاً، تحتوي وحدة ( time ) على دوالَّ لها علاقة بالوقت والتاريخ، وتحتوي وحدة (math ) على
دوالَّ لها علاقة بالعمليات الرياضية، وهكذا.
يُبيِّن الجدول ( 6- 1) مجموعة من الوحدات في لغة البرمجة بايثون Python وأمثلة على الدوالِّ
البرمجية التي تنتمي إلى هذه الوحدات.
أُلاحِظ أنَّ بعض الدوالِّ تستقبل مدخلات، وأنَّ بعضها الآخر لا يستقبل أيَّ مدخلات. فمثلًًا، الدالَّة
(…) sqrt تستقبل رقمًا واحدًا، والدالَّة (…) pow تستقبل رقمين اثنين، والدالَّة (…) median تستقبل
قائمة، في حين أنَّ الدالَّة ( )random لا تستقبل أيَّ مدخل.
أُلاحِظ أيضًا أنَّ جميع هذه الدوالِّ تعمل على إرجاع النتائج بالرغم من أنَّ ذلك ليس شرطًا في
الدوالِّ. فمثلًًا، تطبع الدالَّة (…) print على الشاشة، ولا تُرجِع أيَّ شيء ما يُفسِّر سبب استدعائها
من دون تخزين نتيجتها. أمّا الدالَّة (…) input فتعمل على إرجاع ما أدخله المُستخدِم. ومن ثَمَّ، فإنَّ
الطريقة المُستخدَمة في استدعاء كلٍّ من هاتين الدالَّتين مختلفة:
نحتاج إلى تخزين النتيجة في مُتغيِّر لا نحتاج إلى تخزين أيِّ نتيجة والشيء نفسه ينطبق على الدالَّة
(…) sorted والدالَّة ()sort إذ تعمل الأولى على إرجاع إحدى القوائم المُرتَّبة، في حين تعمل الثانية على ترتيب القائمة نفسها، ولا تُرجِع أيَّ نتيجة، أنظر الجدول(2-6)
استيراد الوحدات ( Import)
تُمثِّل كل وحدة ملفًّا يحوي دوالَّ برمجية خاصة بها إضافةً إلى تعريفات أُخرى ولهذا، فإنَّ
استخدام هذه الدوالِّ يتطلَّب أوَّلًًا استيراد الوحدة باستخدام كلمة( import ) كما في المثال الآتي:
import math
print(math.sqrt(2))
يعمل المُبرمِجون عادةً على وضع جملة الاستيراد في رأس البرنامج حتّى يتضح - ابتداءً- لقارئ
البرنامج أيُّ الوحدات يراد استخدامها. وبعد استيراد الوحدة، يُمكِن استدعاء الدوالِّ الخاصة بها
بنفس الطريقة التي ورد ذكرها في الجدول ( 6- 1) .
أُلاحِظ أنَّه عند استدعاء دالَّة من وحدة ما، فإنَّ اسم هذه الوحدة يوضَع قبل اسم الدالَّة، مثل
math.sqrt(2) . ويُمكِن التخلُّص من ذلك باستيراد الدالَّة نفسها، مثل الدالَّة sqrt التي تُستورَد من
وحدة( math ) كما يأتي:
from math import sqrt
print(sqrt(2))
بعد ذلك تُستخدَم الدالَّة sqrt مباشرة من دون حاجة إلى اسم الوحدة، ولكنْ يجب الحذر حينئذٍ؛ لأنَّ هذه الجملة لا تستورد الوحدة، وإنَّما تستورد فقط الدالَّة؛ لذا لا يُمكِن بعد استيرادها استخدام دالَّة أُخرى من الوحدة نفسها من دون استيرادها أو استيراد كامل الوحدة كما أشرنا آنفًا.
تعريف دوالَّ برمجية جديدة
قد يحتاج المُستخدِم إلى تعريف دوالَّ برمجية خاصة به؛ إمّا لتنظيم البرامج، وإمّا لاحتواء هذه
الدوالِّ على وظائف تَلزمه في البرنامج بصورة مُتكرِّرة، وإمّا لاستدعاء آخرين هذه الدوالَّ للإفادة
منها.
يُبيِّن المثال الوارد في الشكل ( 6- 1) كيفية تعريف دالَّة بسيطة تُسمّى random_greeting ، وتُبادِر إلى
إلقاء تحية عشوائية كلَّما تمَّ استدعاؤها:
يجب تعريف الدالَّة بكتابة كلمة ( def ) متبوعةً باسم الدالَّة، ثمَّ بأقواس ونقطتين رأسيتين. ويُمكِن
للمُفسِّر التمييز بين ما بداخل الدالَّة وخارجها عن طريق مسافة البَدْء التي تكون في أوَّل السطر.
بعد تعريف الدالَّة، يُمكِن استدعاؤها في أيِّ مكان داخل الملف نفسه كما يأتي:
يُبيِّن الشكل ( 6- 2) برنامجًا مُتكامِلًًا يعمل على تعريف دالَّتين واستدعائهما. ولمّا كانت الدوالُّ لا
تُنفَّذ إلا بعد استدعائها، بِغَضِّ النظر عن مكانها في الملف، فإنَّ البرنامج سيبدأ العمل من السطر
الذي يُسأَل فيه عن الاسم، بالرغم من أنَّ هذا السطر يأتي بعد تعريف الدوالِّ.
الحل :
أ - نقل كامل تعريف دالة الوداع random_goodbye لتصبح قبل دالة الترحيب _ random
greeting ، هل أثر ذلك على صحة عمل البرنامج ولماذا؟
لا يؤثر على صحة عمل البرنامج ، لأن الدوالُّ لا تُنفَّذ إلا بعد استدعائها بِغَضِّ النظر عن مكانها في الملف
ب - نقل كامل تعريف الدالتين ليصبحا في نهاية البرنامج بدلاً من بدايته )أي بعد الجمل
التي تسأل عن الاسم والعمر وتقوم باستدعاء الدوال( ، هل أثر ذلك على صحة عمل
البرنامج ولماذا؟
لا يؤثر على صحة عمل البرنامج ، لأن الدوالُّ لا تُنفَّذ إلا بعد استدعائها بِغَضِّ النظر عن مكانها في الملف
يتطلَّب استخدام بعض الدوالِّ توافر مجموعة من البيانات، تعمل الدوالُّ على تحليلها ومعالجتها.
فمثلًًا، لا يُمكِن استخدام دالَّة الجذر التربيعي قبل استقبال رقم يُمكِّن من حساب جذره التربيعي.
ولهذا يجب تحديد عدد مدخلات ( أو مُعامِلات) الدالَّة (parameters) عند تعريفها، وإعطاء كل
مدخل (مُعامِل) اسمًا.
إرجاع النتائج
إنَّ الدوالَّ التي عَمِلْنا على تعريفها في الأمثلة السابقة لا تُرجِع أيَّ نتيجة. والمثال الوارد في
الشكل ( 6- 4) يُبيِّن كيف يُمكِن إرجاع نتيجة من دالَّة، وكيف تُستقبَل النتيجة عند استدعاء الدالَّة.
عند تشغيل البرنامج، ستظهر النتيجة الآتية على شاشة جهاز الحاسوب:
78.53981633974483
78.53981633974483
الحل :
عند تشغيل البرنامج، ستظهر النتيجة الآتية على شاشة جهاز الحاسوب كما في الشكل :
تعمل الدالَّة في المثال السابق على استقبال رقم يُمثِّل نصف قُطْر دائرة، وإرجاع مساحة هذه الدائرة،
علمًا بأنَّ عملية إرجاع النتيجة تمَّت باستخدام return إذ أُنهِي تنفيذ الدالَّة عند تلك النقطة، وتمَّ
إرجاع النتيجة. ولهذا لن يُنفَّذ أيُّ أمر يأتي بعد هذه الجملة في الدالَّة في حال وجود أيِّ سطر
يُمكِن استخدام أكثر من جملة return في الدالَّة نفسها، بحيث تُنفَّذ كل جملة في حالة مختلفة
عن الأُخرى، أنظر الشكل( 6- 5) الذي يُبيِّن مثالًًا على دالَّة تعمل على إرجاع حرف مختلف يُمثِّل
مُعدَّل الطالب بحسب علامته:
الشكل ( 6- 5) مثال على استخدام جملة ( return) لإرجاع نتيجة ما.
def letter_grade(average_grade): def calculate_average(grades): grades = [] for i in range(num_grades): average_grade = calculate_average(grades) final_grade = letter_grade(average_grade) print(f"المُعدَّل: {average_grade}") |
الخطأ :
def a bsolute_value(x):
التصحيح :

النوع من المُتغيِّرات اسم المُتغيِّرات المحلية local variables فهي مُعرَّفة فقط محليًّا داخل الدالَّة.
أمّا مدى كلٍّ من المُتغيِّر AVOGADRO والمُتغيِّر result فغير محصور داخل الدالَّة؛ لذا يُطلَق
على هذا النوع من المُتغيِّرات اسم المُتغيِّرات العامة global variables
يُذكَر أنَّ المُتغيِّر atoms والمُتغيِّر moles المُعرَّفين داخل الدالَّة الأولى هما مُتغيِّران مختلفان عن
المُتغيِّر atoms والمُتغيِّر moles المُعرَّفين داخل الدالَّة الثانية؛ فكلٌّ من هذه المُتغيِّرات مُعرَّف
محليًّا في مدى ما، ولا علاقة له بالمُتغيِّرات المُعرَّفة في المدى الآخر.

Stack Frame إذ يُستعمَل هذا المكان لحفظ كل ما يتعلَّق بالدالَّة من مُتغيِّرات محلية، ثمَّ يُتخلَّص
منه عند الانتهاء من تنفيذ الدالَّة.
لتوضيح ذلك، أتتبَّع البرنامج الوارد في الشكل( 6- 8)

- تعريف المُتغيِّر x في المدى العام بقيمة 7 .
- استدعاء الدالَّة fun1 ، ثمَّ إرسال القيمة 7 إلى مُتغيِّر محلي داخل الدالَّة، يُسمّى x
- تغيير قيمة المُتغيِّر المحلي x إلى 3 .
- انتهاء الدالَّة، والتخلُّص من المُتغيِّر المحلي x الذي يحمل القيمة 3 .
- العودة إلى المدى العام، حيث يوجد مُتغيِّر يُسمّى x وتبلغ قيمته 7 .
- استدعاء الدالَّة fun2 ، ثمَّ إرسال القيمة 7 إلى مُتغيِّر محلي داخل الدالَّة، يُسمّى x
- تغيير قيمة المُتغيِّر المحلي x إلى 5 .
- انتهاء الدالَّة، والتخلُّص من المُتغيِّر المحلي x الذي يحمل القيمة 5 .
- العودة إلى المدى العام، حيث يوجد مُتغيِّر يُسمّى x وتبلغ قيمته 7 .
- اكتمال طباعة قيمة المُتغيِّر x

- المتغير العام x قيمته 6
- تم استدعاء الدالة () f تغيرت قيمة المتغير المحلي 1 = x
- العودة لقيمة المتغير العام x = 6
- طباعة قيمة x وهي 6
سلاسل التوثيق
يحرص المُبرمِجون على توثيق المعلومات الأساسية Documenting حول الدوالِّ التي يكتبونها
مثل: الهدف من الدالَّة، ومُعامِلاتها مدخلاتهاوما تعمل على إرجاعه ما يُسهِّل عليهم إعادة
استخدام هذه الدوالِّ.
يُمكِن توثيق الدوالِّ باستخدام الدالَّة help كما في الشكل( 6- 9 ) الآتي:
كذلك يُمكِن توثيق الدوالِّ التي يكتبها المُستخدِم بإضافة شرح داخل سلسلة تعريف الدالَّة مباشرة
كما في الشكل ( 6- 9) الآتي:
يُطلَق على هذه السلسلة اسم سلسلة التوثيق Docstring ويُمكِن للغة البرمجة بايثون Python
تعرُّفها مباشرة، وإدراك أنَّها توثيق للدالَّة؛ فما إنْ تُستخدَم الدالَّة help ، حتّى يظهر الشرح الذي كتبه
المُستخدِم.
تصميم البرامج باستخدام الدوالِّ
بعد تعرُّف العديد من التفاصيل المُتعلِّقة بتعريف الدوال البرمجية واستخداماتها، لا بُدَّ من
استعمالها لتنظيم عملية تصميم البرامج وكتابتها؛ كي تصبح أسهل للقراءة والتصحيح والتعديل
وإعادة الاستخدام.
مثال:
يطلب البرنامج الآتي إلى طالب في مدرسة إدخال بريده الإلكتروني والبريد الإلكتروني الخاص
بأحد والديه، ثمَّ يطلب إليه إعادة إدخال البريد الإلكتروني إنْ لم يكن البريد المُدخَل يتبع نمطًا
صحيحًا.
إذا كُتِب هذا البرنامج من دون استخدام الدوالِّ، فإنَّه سيبدو على النحو الظاهر في الشكل ( 6- 10 ):

للتحقُّق من صحة البريد الإلكتروني، ثمَّ استدعاء هذه الدالَّة داخل حلقة التكرار كما في الشكل
(11-6 )

للتأكُّد من البريد الإلكتروني من دون حاجة إلى إعادة كتابة ذلك الجزء مَرَّة أُخرى.

1 . التفكير في كيفية رسم مُثلَّث مقلوب، وكتابة دالَّة تتولّى عملية الرسم.

والنجوم، وأنَّ عدد الفراغات يزداد في كل سطر بمقدار 1 وعدد النجوم يقل بمقدار 2 .
2 . تعريف دالَّة تستقبل حجم المُثلَّث، مُمثَّلًًا بعدد النجوم في السطر العلوي، وتطبع هذه النجوم
وَفقًا للعلاقة السابقة.
spaces = 0
while n > 0:
print(' '*spaces + '*' * n)
spaces += 1
n -= 2
استنتاج حجم المُثلَّث الأكبر من عدد المُثلَّثات. وبالنظر إلى الأمثلة السابقة، يتبيَّن أنَّ حجم المُثلَّث الأصغر هو 3 وأنَّ حجوم المُثلَّثات تزداد بمقدار 2 وصولًًا إلى عدد المُثلَّثات المطلوب؛ لذا يُمكِن حساب حجم أكبر مُثلَّث باستخدام المعادلة الآتية:
- size = 1 + 2*n ، حيث n عدد المُثلَّثات المطلوب.
- استدعاء دالَّة لرسم مُثلَّث كبير الحجم، ثمَّ تقليل الحجم بمقدار 2 ثمَّ استدعاء الدالَّة مَرَّة
أُخرى لرسم مُثلَّث آخر، وهكذا حتّى يتمَّ الوصول إلى الحجم 1 .
n = int(input("How many triangles? "))
size = 1 + 2*n
while size > 1:
draw_triangle(size)
size -= 2
عند تشغيل البرنامج، سيظهر المُثلَّث الأوَّل على النحو الذي خُطِّط له، خلافًا لبقية المُثلَّثات؛ إذ
ستظهر مائلة بصورة غير صحيحة كما في الشكل الآتي:
تشغيل البرنامج للحصول على النتيجة وارفاق صورة لها
يُعْزى سبب ذلك إلى خطأ في تصميم دالَّة رسم المُثلَّث؛ إذ لا تبدأ جميع المُثلَّثات من أوَّل السطر،
ويجب إزاحة المُثلَّث إلى اليمين كلَّما كان أصغر؛ لذا يجب أوَّلًًا تعديل الدالَّة على نحوٍ يُمكِّنها من
تسلُّم مقدار الإزاحة ( إلى جانب حجم المُثلَّث ) ثمَّ إرسال هذا المقدار إلى الدالَّة، فيبدأ المُثلَّث الأوَّل من دون أيِّ إزاحة، ثمَّ يأخذ كَمُّ الإزاحة يتزايد بمقدار 1 كلَّما قَلَّ حجم المُثلَّث.
def draw_triangle(n, shift):
spaces = 0
while n > 0:
print(' '*shift + ' '*spaces + '*' * n)
spaces += 1
n -= 2
n = int(input("How many triangles? "))
size = 1 + 2*n
shift = 0
while size > 1:
draw_triangle(size, shift)
size -= 2
shift += 1
المواطنةُ الرقميةُ:
- المسؤولية الرقمية: أحرص على الإحاطة بالتطوُّرات الجديدة في مجال الأمان الرقمي والتكنولوجيا، وأُطوِّر مهاراتي باستمرار.
- المشاركة الفاعلة: أُشارِك في المناقشات والمنتديات بفاعلية وإيجابية، وأُسهِم في بناء مجتمع
رقمي صحي.
- التعاون الإلكتروني: أُوظِّف الأدوات الرقمية في العمل الجماعي البَنّاء والتعاون الفاعل مع
الآخرين.
- دعم المبادرات الرقمية الإيجابية: أُشارِك في المبادرات الرقمية التي تُسهِم في تعزيز الوعي
الرقمي والمواطنة الرقمية.