التفريغ الكامل للمحاضرة 7: التفكيك والتجريد والدوال
التفريغ الكامل للمحاضرة 7: التفكيك والتجريد والدوال
المصدر الأصلي: التفريغ الإنجليزي الرسمي بصيغة PDF، المرتبط بـصفحة المحاضرة وصفحة الفيديو.
النسبة والترخيص: آنا بيل (Ana Bell)، مقرر 6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python، خريف 2022، معهد ماساتشوستس للتكنولوجيا، MIT OpenCourseWare. هذه ترجمة عربية غير رسمية؛ لا تعني تأييد MIT أو اعتماده لها. تخضع المواد الأصلية المشمولة وهذه الترجمة لرخصة CC BY-NC-SA 4.0: النسبة إلى أصحابها، والاستخدام غير التجاري، ومشاركة التكييفات بالرخصة نفسها.
منهج المطابقة: تقابل عناوين «صفحة المصدر» فواصل الصفحات في الاستخراج الرسمي 1NWsD4yStlMPsvHXGgvg0oT1zF13vXErk_transcript.txt، وعددها 12 صفحة غير فارغة. تُترجم الفقرات بالترتيب، بما فيها التكرار والتصحيح الشفهي وأسئلة الحضور والإشارات الصوتية. تُحفظ أسماء Python والرموز كما هي، وتُميّز ملاحظات المترجم صراحة عن كلام المصدر. عنوان التسجيل في المصدر: MITOCW | 6.100L Lecture 7 Multi.mp4.
صفحة المصدر 1
[صرير]
[حفيف]
[نقر]
آنا بيل: حسنًا. في المحاضرة الماضية، بدأنا الحديث عن فكرة التفكيك (Decomposition) والتجريد (Abstraction). وتحدثنا قليلًا عن معناهما وكيف يرتبطان بما كنا نفعله بالفعل. اليوم سنأخذ مثالًا من العالم الحقيقي على التفكيك والتجريد، ثم سنرى بالضبط كيف نحققهما في البرمجة.
لنبدأ إذن بالحديث عن مثال في العالم الحقيقي: الهاتف الذكي. كثيرون منا لديهم واحد، لكنه بالنسبة إلى كثيرين منا مجرد صندوق أسود (Black box). أعرف أنه كذلك بالنسبة إليّ. وبالنسبة إلى معظم الناس في العالم، الهاتف صندوق أسود.
ننظر إلى الهاتف أساسًا من حيث مدخلاته ومخرجاته. فيه بعض الأزرار. يمكنك التمرير، ويمكنك لمس الأشياء. لكننا لا نعرف بالضبط كيف تؤدي كل هذه الأزرار والتمرير وهذه الآليات الداخلية عملها.
وفي الواقع، لا نحتاج إلى معرفة كيف تؤدي عملها. بالنسبة إلينا، كمستخدمين، كل ما يهمنا حقًا هو الواجهة (Interface) بيننا وبين المهمة التي نريد إنجازها. فما نحتاج إلى معرفته، أي تلك الواجهة، هو أساسًا العلاقة بين المدخل الذي نعطيه للهاتف والمخرج الذي نحصل عليه. حين نضغط ذلك الزر، ينطفئ الهاتف. وحين نضغط هذا الزر الآخر، يرتفع مستوى الصوت.
وهذه هي فكرة التجريد. فقد جرّد الهاتف أساسًا كل تفاصيل العتاد (Hardware)، وكل تلك التنفيذات التي تجعله يعمل بالفعل للمستخدم. فلا يحتاج المستخدم إلى معرفة كيف يعمل لكي يستخدمه.
والتجريد يتيح التفكيك بالفعل. ماذا يعني ذلك؟ بعد أن نجرّد التفاصيل، يمكن أن تعمل جهات تصنيع مختلفة على مكونات مختلفة من الهاتف لبناء تلك المكونات. وإذا كانت جهات مختلفة تعمل على بناء هذه المئات من الأجزاء المنفصلة داخل الهاتف، كلٌّ على حدة، فلا بد لها من طريقة لإعادة تجميع تلك الأجزاء.
وحين تعمل على أجزائها بصورة منفصلة، فهذه هي فكرة التفكيك. كيف تعرف أن ما تعمل عليه سيلائم بقية المكونات بالفعل؟ إنها تستخدم فكرة التفكيك. تتبع أساسًا مواصفة (Specification). تتبع مجموعة من المدخلات التي قد تدخل مكوّنها، ومجموعة من المخرجات التي قد يحتاج مكوّنها إلى إعطائها لمكونات أخرى.
وكل ما تحتاج إلى معرفته جهات التصنيع المختلفة التي تبني هذه الأجزاء هو جانب الواجهة هذا. لا تحتاج إلى معرفة كيف تبني الجهات الأخرى مكوناتها. كل ما تحتاج إلى معرفته هو الوظائف التي توفرها تلك المكونات الأخرى.
وبهذا تستطيع كل هذه الجهات بناء كل هذه المكونات المختلفة. ستكون الواجهات موحّدة، إن صح التعبير. وهذا كل ما يهمها. وبمجرد معرفة الواجهة، يمكنكم الاجتماع ووضع كل المكونات المختلفة معًا للعمل نحو هدف مشترك، أي جعل الهاتف يعمل.
صفحة المصدر 2
وهذا صحيح للعتاد، كما في مثال الهاتف، لكنه صحيح للبرمجيات (Software) أيضًا. وهذا بالضبط ما سنفعله في محاضرة الدوال (Functions) هذه. سنحقق التفكيك والتجريد في البرمجة: نتعامل مع الشيفرة بوصفها صندوقًا أسود، ونصنع برنامجًا كبيرًا، ونقسّمه إلى هذه الأجزاء المختلفة المكتفية بذاتها.
في البرمجة أيضًا نريد إخفاء التفاصيل، لا في العتاد فقط، كما في الهاتف. نريد إخفاء التفاصيل في البرمجة أيضًا، ونفعل ذلك باستخدام فكرة التجريد هذه. سنكتب الشيفرة، كما كنا نفعل بالفعل، مع التفكير في أن الشيفرة التي نكتبها ستُنجز— ستُكتب مرة واحدة فقط. سيكون لدينا جزء وظيفي من الشيفرة يؤدي مهمة مفيدة جدًا، ثم، بعد كتابته وتنقيحه والتأكد من أنه يعمل جيدًا، سنتعامل معه بوصفه صندوقًا أسود.
ومن ذلك الحين، ما دمنا نعرف المدخلات التي يحتاج إليها هذا الجزء من الشيفرة والمخرجات التي يعيدها إلى شخص آخر أو إلينا، فلا يهمنا بالضبط كيف يفعل ذلك. لا يهمنا إلا أنه موجود ومتاح للاستخدام. في محاضرة اليوم سنرى كيف ننشئ بالفعل هذه الأجزاء الصغيرة الوظيفية من الشيفرة.
يمكننا بعد ذلك إعطاء هذه الأجزاء لأنفسنا. نستطيع بالتأكيد استخدام الأجزاء الوظيفية التي كتبناها، أو إعطاءها لأشخاص آخرين كي يستخدموها أيضًا. سنكتب هذه الأجزاء الوظيفية من الشيفرة. وسنسمّي الـ— سنسمّيها دوال أو إجراءات (Procedures).
وفي الواقع، كنا نستخدم الدوال بالفعل، صدّقوا أو لا تصدّقوا. هذه الثلاث أمثلة لدوال كنا نستخدمها في Python. فـmax دالة: جزء مفيد من الشيفرة، حين نستخدمه بهذه الطريقة تحديدًا، يأخذ مدخلين ويعيد إليّ أكبرهما.
والوسطى، abs، هي دالة القيمة المطلقة (Absolute value). مدخلها عدد واحد، عدد صحيح، وتعيد إليّ القيمة المطلقة لذلك العدد. وlen مفيدة جدًا أيضًا، وكنا نستخدمها مع السلاسل النصية (Strings). مدخلها أساسًا سلسلة نصية، ومخرجها عدد المحارف فيها.
إذن كنا نستخدم الدوال بالفعل. والهدف من محاضرة اليوم أن تبدأوا كتابة دوالكم الخاصة. ونأمل أن تكون مفيدة. فكرة الدالة هي أننا نريد تجريد الكيفية الدقيقة التي تنجز بها الدالة شيئًا مفيدًا، مهمة مفيدة.
والطريقة التي سنخبر بها الآخرين كيف يستخدمون دالتنا هي فكرة التجريد هذه. ونعبّر عما تفعله الدالة بهذه الأشياء المسماة مواصفات. وتُسمى أيضًا سلاسل التوثيق (Docstrings).
وسلسلة التوثيق أشبه بعقد بين شخص ينشئ هذه الدالة المفيدة وشخص يريد استخدامها. قد يكون مستخدم الدالة أنت، من كتبها، أو شخصًا آخر. ومن الأشياء التي سنذكرها في العقد: ما مدخلات الدالة؟ فدالة الطول تحتاج إلى سلسلة نصية.
ما الذي تفعله الدالة؟ وما مخرجها؟ ماذا تعيد الدالة إلى من يستخدمها؟ لم نفعل هذا صراحة بالفعل، لكنكم ربما رأيتموه أثناء كتابة الشيفرة.
إليكم مثال دالة القيمة المطلقة. تظهر معها هذه النافذة الصغيرة المنبثقة كلما كتبتموها. مثلًا abs، ثم قوس، هنا، أو إذا مررتم مؤشر الفأرة فوق دالة في محرر الملفات، سترون مربع النص الصغير هذا يظهر وفيه المواصفة أو سلسلة التوثيق.
صفحة المصدر 3
وهنا ترون توقيع الدالة (Signature) بالضبط. تأخذ مدخلًا واحدًا، x، القيمة التي تريدون إيجاد قيمتها المطلقة، ثم بعض النص هنا الذي يصف ما تفعله الدالة. فالمواصفة أو سلسلة التوثيق ليست حرفيًا إلا تعليقًا متعدد الأسطر. لا شيء خاصًا فيها. ما دمتم تغطّون تلك النقاط، المدخلات وما تفعله الدالة وما تعيده إليكم، فقد كتبتم مواصفة جيدة.
وعليّ أيضًا أن أذكر أن هذه العقود، رغم أنني أسميها عقودًا، لا تفرضها Python بالفعل. فالأمر راجع حقًا إلى كاتب الشيفرة ليتأكد من أن مواصفته مفصلة جدًا وأن الدالة تفعل ما يقول إنها ستفعله. فإذا كانت دالتك تستطيع أخذ أعداد صحيحة موجبة وسالبة مثلًا، فمن الأفضل أن تتأكد من أن الدالة نفسها، وهي تؤدي مهمتها، تستطيع التعامل مع الأعداد الصحيحة الموجبة والسالبة معًا.
وحين نكتب هذه الدوال، يصبح لدينا هذه الأجزاء الصغيرة من الشيفرة التي تؤدي مهمة مفيدة. تُعطى بعض المدخلات، فتؤدي المهمة وتعيد إليّ بعض المخرجات. والآن بعد أن صار لدينا هذه الأجزاء المختلفة الصغيرة من الوظائف، يمكننا أخذ هذا الملف الكبير من الشيفرة الذي قد نكتبه من الآن فصاعدًا، ورؤية الأجزاء التي ربما تتكرر بالضبط.
وهذا دليل على أنها شيء يمكن تجريده في وحدة صغيرة (Module)، أي دالة. ثم يمكنك استخدام هذه الدوال لتقسيم الشيفرة، قطعة كبيرة جدًا من الشيفرة، إلى وحدات أصغر مكتفية بذاتها. وربما يكون معظم عمل الشيفرة مجرد القول: أيتها الوحدة، أعطيني هذه الإجابة من فضلك. ثم: أيتها الوحدة الأخرى، أعطيني هذه الإجابة، ثم جمع هذه القيم من جديد.
تُسمى هذه الأجزاء القابلة لإعادة الاستخدام دوال أو إجراءات. سنحاول أساسًا التعبير عن نوع من الحساب، مهمة مفيدة جدًا تريد أداءها مرارًا. وسنرى بعض التفاصيل عن كتابة الدوال، لكنها في الجوهر شيفرة كنتم تكتبونها بالفعل، مكتوبة فقط بطريقة خاصة تجعلها قابلة لإعادة الاستخدام. لذا سنحتاج الآن إلى تغيير طريقتنا في التفكير في الشيفرة قليلًا، مع تقديم الدوال.
لأننا حتى الآن، حين كنا نكتب دوال في ملف، نكتب أساسًا بعض الشيفرة من الأعلى إلى الأسفل، ثم نفكر فيها على أنها تُنفّذ سطرًا سطرًا من الأعلى إلى الأسفل. لكن بما أننا ننشئ الآن أشياء تسمى دوال، كتلًا صغيرة من الشيفرة يمكن استخدامها مرات كثيرة في أماكن مختلفة من الشيفرة، فسندخل فكرة تعريف الدالة (Defining a function). أي سنكتب جزءًا من الشيفرة، وكل ما يفعله هذا الجزء هو إخبار Python أن هذه وحدة أو دالة موجودة في برنامجي. كل ما نفعله هو تعريف الدالة.
لكننا لن نشغّل الدالة بالفعل حين نعرّفها. وهذا هو الاختلاف الذي سنحتاج إلى تغيير تفكيرنا بشأنه. عندما تعرّف دالة، فأنت تخبر Python فقط أن هنا جزءًا مفيدًا من الشيفرة موجودًا ويفعل شيئًا، لكنه لا يُشغّل بالفعل حتى تستدعي الدالة.
والرائع في كتابة الدالة أنك بعد كتابتها مرة واحدة، تستطيع إجراء 100 استدعاء مختلف للدالة (Function calls) إلى قطعة الشيفرة الوحيدة التي كتبتها، لاحقًا في برنامجك. تستطيع استدعاء الدالة مرات كثيرة بمدخلات مختلفة لتعطيك مخرجات مختلفة، لكنك لم تحتج إلى كتابتها إلا مرة واحدة. وسأقارن هذا بكتابة الشيفرة في ملف.
صفحة المصدر 4
حين نكتب الشيفرة في ملف، نعم، يمكننا كتابة مجموعة كبيرة من الأسطر، لكن الشيفرة لا تعمل أثناء كتابتنا لها. علينا بالفعل الضغط على زر التشغيل لتشغيل الملف. وبالمثل، حين نخبر Python أنني سأنشئ هذه الدالة التي تفعل شيئًا مفيدًا، فهي لا تُشغّل تلك الأسطر بالفعل. علينا إخبار Python أننا نريد تشغيل هذه الدالة.
لذلك أول ما سنفعله في المحاضرة هو إنشاء دالة بالفعل. سأريكم كيف تعرّفون دالة، أي تخبرون Python أن هذه الدالة موجودة. ثم سنرى كيف نشغّل الدالة بالفعل لتعطينا بعض القيم المفيدة.
أما خصائص الدالة فهي: يجب أن يكون لها اسم. تمامًا كما تنشئون متغيرات تخزن قيمة مفيدة، مثل pi إلى 20 منزلة عشرية تريدون إعادة استخدامها مرارًا، سننشئ دالة، ويكون ذلك الاسم أشبه بمقبض نستخدمه للإشارة إلى هذه القطعة الكبيرة من الشيفرة التي تفعل شيئًا مفيدًا لنا. للدالة بعض المدخلات تُسمى معاملات (Parameters) أو وسائط (Arguments). قد لا يكون لها مدخلات، أو أكثر، أو مدخل واحد أو أكثر.
وينبغي أن تكون للدالة سلسلة توثيق. وهي المواصفة. مرة أخرى، مجرد تعليق متعدد الأسطر يخبر المستخدم، الشخص الذي يريد استخدام الدالة، بالمدخلات وما تفعله الدالة وما المخرج، أو القيمة المعادة، أو أي شيء ستفعله له الدالة.
ثم إن جسم الدالة (Function body) ليس إلا شيفرة Python، بالضبط من النوع الذي كنا نكتبه، لكن من دون تغليفه في دالة. إذا وجدت نفسك تكتب جزءًا من الشيفرة يفعل شيئًا مفيدًا، يمكنك بالتأكيد تغليفه في دالة. وسنرى كيف نفعل ذلك اليوم.
فجسم الدالة مجموعة من أسطر الشيفرة تؤدي هذه المهمة المفيدة. والاختلاف الوحيد في الجسم هو أن الدالة يجب أن تنتهي عند نقطة ما. تكون قد أنجزت مهمتها، وحددت قيمة نهائية، هذا الشيء المفيد الناتج عن مهمتك، وتريد إعطاء هذه القيمة إلى من يستخدم هذه الدالة.
ونفعل ذلك باستخدام الكلمة المفتاحية return، كما سنرى في الشريحة التالية. هذا مثال لدالة بسيطة جدًا. وهو تعريف فقط. مرة أخرى، أسطر الشيفرة هذه لا تعمل. هنا نخبر Python فقط أننا ننشئ دالة تفعل شيئًا.
نبدأ بالكلمة المفتاحية def، أي التعريف. لاحظوا أنها زرقاء. إذا كتبتموها في شيفرتكم، سترون لونها يتغير. إذن def تخبر Python أننا نعرّف دالة.
ثم يأتي اسم الدالة. ينبغي أن يكون اسمًا وصفيًا. وعادة يكون كلمة تدل على فعل، لأن الدالة تفعل شيئًا. تريدون إذن اسمًا من نوع الأفعال لدالتكم.
ثم لدينا أقواس. داخل الأقواس تضعون أي مدخلات أو معاملات أو وسائط للدالة. ما الذي ينبغي أن يعطيه المستخدم مدخلًا لهذه الدالة؟ ثم بالطبع نقطتان رأسيتان.
في ذلك السطر، الشيء الوحيد «القابل للتخصيص»، بين علامتي اقتباس، هو اسم الدالة والمعاملات. إذا لم توجد معاملات فلا تضعون شيئًا هناك. وإذا كان هناك أكثر من واحد، تفصلون بينها بفواصل. ينبغي أن يكون كل شيء آخر ثابتًا: def والأقواس والنقطتان الرأسيتان في النهاية.
صفحة المصدر 5
بما أن لدينا نقطتين رأسيتين في النهاية، فعلينا إزاحة الجزء التالي من الشيفرة إلى الداخل. ستخبر المسافة البادئة (Indentation) Python أن بقية هذا جزء من الدالة. فكل ما يأتي من هنا فصاعدًا جزء من تعريف الدالة. ولدينا سلسلة التوثيق بالطبع.
تبدأ بثلاث علامات اقتباس وتنتهي بثلاث علامات اقتباس. وداخلها يمكنك كتابة ما تريد. تعامل معها كتعليق موزّع على أسطر متعددة. وترون هنا أنني قلت إن هذه الدالة تأخذ مدخلًا، i، أقصره على أن يكون عددًا صحيحًا موجبًا.
ثم أقول ما الذي يعيده المدخل إلى المستخدم. ستعيد True إذا كان i عددًا زوجيًا، وFalse خلاف ذلك. وهكذا غطيت كل النقاط: المدخلات وما تفعله الدالة وما تعيده إلى من يريد تشغيلها.
وبعد ذلك لدينا جسم الدالة. لاحظوا أنه مجرد أسطر شيفرة كنتم ستكتبونها على أي حال. لو أُعطيتم مسألة في اختبار قصير تقول: مع تعريف i لكم، اكتبوا شيفرة تطبع True إذا كان العدد زوجيًا وFalse إذا كان فرديًا، فهذه أساسًا أسطر الشيفرة التي ستكتبونها.
والاختلاف الوحيد هو return الصغيرة هنا. الدالة أسطر شيفرة تؤدي مهمة. وحين تنتهي هذه المهمة، لا بد أن تعيد شيئًا. لا يمكنها مجرد البقاء هناك، على ما أظن.
ويُحدَّد ما تعيده إلى من يريد تشغيلها بعبارة return هذه. فإذا كان العدد قابلًا للقسمة على 0، نعيد True، وإلا نعيد False. فستُعاد إحدى القيمتين، True أو False، من الدالة. ويمكنكم التفكير في هذا بوصفه مخرج الدالة.
ملاحظة المترجم: وردت «القسمة على 0» هكذا في التفريغ الرسمي. يوضح الكلام في الصفحة التالية أن الاختبار المقصود هو أن يكون باقي قسمة
iعلى2مساويًا لـ0؛ لا تُستبدل العبارة الأصلية هنا صمتًا.
حسنًا، هل من أسئلة حتى الآن؟ هل هذا مفهوم؟ مرة أخرى، نحن فقط ننشئ هذه الدالة داخل الحاسوب، داخل Python. لا نشغّل أسطر الشيفرة هذه بالفعل بعد.
إذا أُعطيتم مسألة أكبر، أريد تخصيص شريحتين للحديث عن طريقة التفكير في كتابة الدالة. هذه كانت سهلة جدًا. من الواضح أن كتابتها ليست صعبة جدًا، لكن ما عملية التفكير إذا أُعطيتم مسألة أكبر، بالإنجليزية أو نحو ذلك، وأردتم تحويلها إلى قطعة شيفرة تفعل شيئًا مثيرًا للاهتمام وظيفيًا؟
تفكرون في ماهية المسألة. مسألتنا: مع إعطائنا عددًا صحيحًا، نحدد هل هو زوجي أم فردي. انطلاقًا من هذه العبارة، يمكنكم التوصل إلى اسم لقطعة الشيفرة هذه ذات الوظيفة المفيدة. فـis_even اسم جيد.
ويمكننا أيضًا التوصل إلى مدخلات الدالة. i: لم نُعطَ إلا عددًا واحدًا، فلا حاجة إلى أن تأخذ الدالة أي مدخلات أخرى. وباستخدام هذا الوصف، نستطيع الآن البدء في ملء سلسلة التوثيق التي تقول إن مدخلنا سيكون عددًا صحيحًا موجبًا. يمكننا استخدام الرياضيات لتحديد قيود المدخلات. ثم نكتب بقية سلسلة التوثيق التي تخبرنا ماذا نعيد ومتى، وما الذي تفعله الدالة.
صفحة المصدر 6
وبعد ذلك تستطيعون حل المسألة. نحلها بالقول: إذا كان الباقي عند قسمة i على 2 هو 0، نعيد True، وإلا نعيد False. وهذه شيفرة كان يمكنكم كتابتها بالفعل من دون محاضرة الدوال هذه. لكننا الآن نضعها في سياق تعريف دالة. سنستطيع تشغيل هذه الدالة بمدخلات مختلفة كثيرة لتعطينا مجموعة من المخرجات المختلفة: هل كل هذه الأعداد المختلفة زوجية أم لا؟
حين نكتب جسم الشيفرة، الاختلاف الوحيد عما كنتم تفعلونه هو عبارة return. بدلًا من طباعة شيء في وحدة التحكم (Console)، سنعيد قيمة إلى من يريد معرفة هل العدد i زوجي أم فردي. يمكن أن تطبع الدالة أشياء في وحدة التحكم أيضًا. لكن الأمر الأساسي هنا أنك تريد إعادة قيمة إلى المستخدم.
وبعد كتابة الشيفرة مباشرة واختبارها والتأكد من أنها تعمل، يمكنكم تحسينها قليلًا. هنا نحسّنها بملاحظة أن i%2==0 هنا قيمة منطقية (Boolean) بالفعل. إذا كان i زوجيًا، فإن 3%2==0 هي True. وخلاف ذلك فهي False بالفعل.
ملاحظة المترجم: ورد المثال
3%2==0ووصفه بـTrueفي المصدر هكذا؛ قيمة هذا التعبير في Python هيFalse، لأن3فردي. يُحفظ التصريح الأصلي مع توضيح هذا التعارض.
فهذه الأسطر الأربعة تقول أساسًا: إذا كان صحيحًا، فأعد True؛ وإلا فأعد False. يمكن أن يكون تحسيننا مجرد إعادة نتيجة i%2==0 مباشرة. سنعيد هنا إما True وإما False بناءً على قيمة i.
وعند هذه النقطة— مرة أخرى، آسفة لأنني أؤكد هذا بما يكفي— أكثر من اللازم. لكن من المهم جدًا أن نفهم أنه بمجرد كتابة هذه الأسطر في سياق تعريف دالة، فإنها لا تعمل. تبقى أساسًا في Python، وتقول إن هناك أسطر شيفرة تقابل كائن دالة اسمه is_even. هذا كل شيء.
ما نحتاج إلى فعله الآن هو إخبار Python بالفعل بتشغيل هذه الأسطر. ولنفعل ذلك نجري استدعاء دالة. ومرة أخرى، كنا نجري استدعاءات دوال بالفعل، لكن لدوال موجودة مسبقًا في Python، في Python نفسها، مثل max والقيمة المطلقة وlen وكل ذلك. لكننا الآن نستدعي شيئًا كتبناه نحن، هذه القطعة الجميلة من الشيفرة التي تخبرنا هل العدد، المدخل، زوجي أم لا.
سأستدعي هنا اسم دالتي، أي سأنادي اسم دالتي. أكتب أساسًا اسم الدالة في الشيفرة، ثم أقواسًا، ثم المدخلات التي تتوقعها الدالة. لا يوجد إلا واحد: العدد الذي أريد تحديد هل هو زوجي أم فردي. ثم هذا كل شيء.
لديّ اسم دالتي، ثم كل المدخلات، المعاملات التي تتوقعها هذه الدالة. عند هذه النقطة تدخل Python جسم الدالة، وتُشغّلها، وتعيد قيمة. فأيًا تكن القيمة المرتبطة بـreturn، تُعاد هذه القيمة فورًا إلى من استدعاها. ماذا يعني ذلك؟
حسنًا، ستحل تلك القيمة المعادة محل استدعاء الدالة بالكامل. لنعد بالتفكير إلى التعبيرات (Expressions). أتذكرون حين كنا نتعلم تعبيرات Python وقلت إن لديكم شيئًا مثل كائن، عامل، كائن، مثل 3 + 2؟ كان هذا تعبيرًا.
دخلت Python وقيّمت ذلك التعبير، واستبدلت التعبير كله بالقيمة 5. هذا هو الشيء نفسه بالضبط. في الواقع، الدوال تشبه تعبيرات Python نوعًا ما. تفعل شيئًا مفيدًا. الفرق فقط أن ما يُقيَّم ليس حسابًا رياضيًا أو نحو ذلك، بل مجموعة أسطر شيفرة تُقيَّم.
صفحة المصدر 7
لكن في النهاية تعيد الدالة قيمة واحدة فقط. وتحل هذه القيمة محل استدعاء الدالة بأكمله. سيُستبدل هذا الاستدعاء كله أساسًا بـFalse، لأنه عدد فردي. وسيُستبدل التالي بـTrue، وهي القيمة المعادة من الدالة.
وهذا هو شكل الشيفرة: مجرد تعريف is_even، ثم تشغيل استدعاء دالة. هذا كل ما سيكون في ملفنا. هنا تعريف الدالة، ثم، عند مستوى المسافة البادئة نفسه، استدعاء الدالة، لأن الاستدعاء ليس جزءًا من الدالة. الاستدعاء يستخدم الدالة التي كتبناها فحسب.
ما الذي يحدث بالضبط؟ سنمضي الآن قليلًا خطوة خطوة، بتفصيل أكبر عما يحدث بالضبط حين نستدعي الدالة. حين نجري الاستدعاء— مرة أخرى، تعريف الدالة يخبر Python فقط أن لدينا هذه الدالة التي تفعل شيئًا في برنامجنا، ثم هنا لدينا استدعاء الدالة.
بمجرد أن ترى Python استدعاء الدالة، تبدأ فعل شيء مفيد. أما هنا في الأعلى، فتخزن هذا في الذاكرة نوعًا ما. بمجرد أن ترى استدعاء is_even مع 3، تنظر إلى معامل الإدخال في الاستدعاء. وترون هنا قيمة. إنها كائن فعلي ملموس. ليست متغيرًا عشوائيًا، ولا شيئًا مجردًا، بل العدد 3.
أما i هنا في تعريف الدالة فيُسمى معاملًا شكليًا (Formal parameter). إنه مجرد. كتبنا جسم الدالة بافتراض أن المستخدم سيعطينا في النهاية قيمة لـi. لكن داخل جسم الدالة نفسه، i مجرد متغير نستخدمه.
يشبه ذلك ما في الاختبارات القصيرة. كنت أقول حتى الآن: افترض أن لديك عددًا n معرّفًا لك، واكتب الشيفرة بافتراض هذا العدد. إنه الشيء نفسه بالضبط. نكتب شيفرة جسم الدالة بافتراض أننا نعرف قيمة لـi.
حين ترى Python استدعاء الدالة مع 3، تدخل جسم الدالة وتقول: حسنًا، ما معاملاتي؟ يوجد واحد فقط، وهو i. وستطابقها واحدًا واحدًا مع كل المعاملات الفعلية (Actual parameters) المعطاة في الاستدعاء. أي تطابق i مع 3 ببساطة.
ثم تنفّذ جسم الدالة. فتستبدل كل موضع ترون فيه i. قد تكون لدينا قطعة أطول من الشيفرة هنا، لكن لدينا سطر واحد فقط. تستبدل i بـ3، فيكون لدينا 3%2==0. الآن لدينا قيمة ملموسة، False.
يُستبدل هذا التعبير بـFalse. ولذلك سيعيد هذا السطر من الشيفرة False. وبمجرد أن ترى Python تلك القيمة المعادة، تخرج فورًا من الدالة وتعطي القيمة التي تعيدونها إلى من استدعاها. فسيُستبدل هذا الاستدعاء كله هنا بـFalse.
كان هذا تفصيلًا شديدًا خطوة خطوة، لكن هل هو مفهوم؟ حسنًا. هذا برنامج لا يفعل شيئًا. إذا كتب أحد هذا البرنامج وشغّله، فلن يعرض في الواقع شيئًا للمستخدم. لأن الأمر في برنامجنا كما لو كتبنا سطر شيفرة يقول فقط False. هل يُطبع هذا في المخرج؟ لا، أليس كذلك؟
علينا فعل شيء مفيد الآن بعد حصولنا على نتيجة استدعاء الدالة. ومن الأشياء المفيدة التي نستطيع فعلها طباعة نتيجة استدعاء الدالة بالفعل. هنا لدينا print، ثم لدي استدعاء الدالة الذي كان هنا في الأعلى. أضعه فقط داخل عبارة print.
صفحة المصدر 8
وكما من قبل، ستقيّم Python التعبير is_even(3). يُستبدل بـFalse، فيصبح هذا السطر أساسًا طباعة False. وهذا هو شكله في شيفرتنا الفعلية.
هنا لدي دالة is_even، بالطريقة غير الكفؤة لكتابتها. لدي استدعاءان للدالة هنا. لكن إذا شغّلت الشيفرة فلن تطبع شيئًا. عليّ فعل شيء مفيد بهما. ومن الأشياء المفيدة طباعة نتيجة هذين الاستدعاءين. والآن بعدما وضعت الاستدعاءين داخل عبارة print، أرى المخرج في وحدة التحكم.
نحن نكتب— نفصل أنفسنا نوعًا ما الآن حين نكتب الشيفرة. أولًا نعرّف دالة، بعض الشيفرة التي تفعل شيئًا مفيدًا، ثم ثانيًا نستخدم هذه الدالة التي كتبناها لإجراء استدعاءات دوال.
وجمال كتابة الدالة أننا نكتبها مرة واحدة وننقحها مرة واحدة، لكننا الآن نستطيع تشغيلها أي عدد من المرات نريده. من دون الدوال سنجد أنفسنا ننسخ قطعة الشيفرة المفيدة ونلصقها في مواضع كثيرة، مما قد يؤدي إلى أخطاء. تصبح الشيفرة صعبة التعديل وصعبة التنقيح، وكل ذلك.
سأعطيكم فرصة لتجربة هذا نحو دقيقة. لنطلب منكم كتابة هذه الشيفرة. هنا أعطيكم مواصفة الدالة. وسأعطيكم إياها معظم الوقت، حتى في الاختبارات القصيرة. أريد منكم كتابة دالة تسمى div_by.
تأخذ هذه معاملين، كلاهما عدد صحيح أكبر من 0، وd. وتعيد هذه الدالة True إذا كان d يقسم n من دون باقٍ، وFalse إذا لم يقسم n من دون باقٍ. وإذا اختبرتموها بهاتين القيمتين، ينبغي أن تعطينا الأولى False، والثانية True.
وكالعادة، يوجد هذا في ملف Python بالأسفل. قرب السطر 28 ينبغي أن تبدأوا كتابة شيفرتكم. هل لدى أحد بداية يعطيني إياها؟ ينبغي أن تكون مشابهة جدًا لما فعلناه للتو— نعم؟
أحد الحضور: e%n==0، ثم اطبعي True.
آنا بيل: وإلا اطبعي False. لنشغّل الدالة. آه، لنفعل ذلك بواحدة فقط. أتوقع من الأولى طباعة False. إنها تطبع False بالفعل، لكنها تطبع أيضًا هذه None الغريبة بعدها مباشرة.
في الواقع، هذا شيء نريد— سنتحدث عنه في المحاضرة التالية. لكن هل يعرف أحد تحسينًا يمكننا إجراؤه على الشيفرة؟ نعم.
أحد الحضور: [غير مسموع]
آنا بيل: نعم، بالفعل. أنت محق. بدلًا من طباعة True، تذكروا أنها دالة. نريد منها أن تعيد إلينا القيمة True، أليس كذلك؟ بدلًا من الطباعة، سنكتب return True. ولا نحتاج إلى الأقواس في هذه الحالة. ثم نكتب return False.
والآن لم تعد لدينا تلك None الغريبة بعدها مباشرة. هذا شيء كنت سأتحدث عنه في المحاضرة التالية. لكن أساسًا، حين كانت لدينا print هنا، ماذا أعادت الدالة؟ هل كان داخلها عبارة return؟ لا، أليس كذلك؟
إذا لم توجد عبارة return داخل الدالة، تعيد Python تلقائيًا القيمة الخاصة None. سنتحدث عن هذا بتفصيل أكبر في المحاضرة التالية. لكن return True وreturn False صحيحتان هنا. نعم؟
صفحة المصدر 9
أحد الحضور: هل تحتاجين إلى التفاصيل، أم تعيدين فقط—
آنا بيل: نعم. نعم. لا تحتاج إلى return. إلى if else، كما من قبل. يمكننا إعادة هذا مباشرة فقط. ويمكننا تشغيلها مع المثال الآخر. ينبغي أن تعيد الثانية True بالفعل، لكنها أعادت False. هل يعرف أحد المشكلة؟ نعم؟
أحد الحضور: لها باقٍ.
آنا بيل: نعم، بالضبط. نريد في الواقع الباقي عند قسمة n على d. فهذا معكوس فحسب، و%d==0. نعم. من الجيد أن لدينا حالتي اختبار لاختبار ذلك. وليس عليكم اختبارها بهذه الأعداد الكبيرة. يمكنكم بالطبع اختبارها بأعداد أصغر أيضًا.
لنبتعد قليلًا ونناقش كيف تُخزن الدوال بالضبط في الذاكرة. فقد ذكرت هذا الأمر عن تعريف الدالة، وأنه لا يفعل شيئًا حقًا يمكننا رؤيته. لكن ماذا يحدث بالضبط في الذاكرة؟ لنفكر فيما يحدث حين ننشئ متغيرات.
حين ننشئ a = 3 داخل الذاكرة، أو نطاق البرنامج (Program scope)— مرة أخرى، سنتحدث عن هذا في المحاضرة التالية. لكن يمكنكم التفكير فيه بوصفه الذاكرة. ما يحدث هو أن a يصبح متغيرًا مرتبطًا بالقيمة 3. وb = 4 متغير b مرتبط بالقيمة 4. وسيكون c مرتبطًا بالقيمة 7. واضح، صحيح؟ نعرف هذا بالفعل.
ماذا يحدث حين ننشئ دالة؟ مرة أخرى، هذا شيء قد أكتبه في ملف شيفرة. الجزء العلوي تعريف دالتي. بمجرد أن ترى Python الكلمة المفتاحية def، يكون كل ما له مسافة بادئة ويُعد جزءًا من تعريف الدالة في الجسم مجرد شيفرة أساسًا.
بالنسبة إلى Python، لا يهمها عند هذه النقطة ما تلك الشيفرة أو ما تفعله. كل ما تعرفه أن هناك كائن دالة (Function object)— والدوال كائنات بالفعل في Python. هناك كائن دالة اسمه is_even. هذا كل ما تعرفه حين نصل إلى هذه النقطة من الشيفرة، مباشرة بعد تعريف الدالة، وقبل a = مباشرة.
نفكر في الدالة على أنها تشبه «متغيرًا»، بين علامتي اقتباس. ليست متغيرًا بالفعل، لكنها مثل متغير اسمه is_even، ويشير إلى— يرتبط ببعض الشيفرة في الذاكرة. ولا يهمنا الآن ما تلك الشيفرة، لأننا قد لا نستخدمها أبدًا. لا يهمنا ما الشيفرة إلا حين نجري استدعاءات الدوال.
هنا في الأسفل يحدث العمل بالفعل، حين نستدعي دوالنا. لدي a، وسيكون كالمعتاد متغيرًا، يرتبط بقيمة ما. فتعريف الدالة أشبه بصندوق أسود. بعد أن تكتبه مرة ويعمل، لا يعود يهمك كيف ينجز مهمته فعلًا. كل ما يهمك أنه يأخذ عددًا ويخبرك هل هو زوجي أم فردي عن طريق True وFalse.
هنا في الأسفل، حيث نجري استدعاءات الدالة، نستخدم صندوقنا الأسود فحسب. ونستخدمه بإجراء استدعاءات دوال. سيكون a متغيرًا مرتبطًا بالقيمة التي تعيدها is_even. وبناءً على الاستدعاء ستكون False.
ثم هنا لدي استدعاء آخر للدالة. أستخدم قطعة الشيفرة المفيدة التي كتبتها في الأعلى. سيكون b متغيرًا مرتبطًا بـTrue، وسيكون c متغيرًا مرتبطًا بـTrue. هل هذا مفهوم؟ نفصل نوعًا ما الشيفرة التي نكتبها، والتي لا تعمل حتى نستدعي الدوال بالفعل. هذا هو أمر الدوال، وهذه هي الطريقة التي تساعدنا بها على كتابة شيفرة أكثر متانة (Robust).
صفحة المصدر 10
والآن يمكن أن يكون لدينا هنا جزء أكثر تعقيدًا من الشيفرة نستخدم فيه الدالة التي كتبناها. لا مجرد استدعاء دالة وطباعة النتيجة، بل استخدامها فعلًا داخل برنامج أكثر إثارة للاهتمام. لدي هنا برنامج سيطبع لي الأعداد بين 1 و10، وسيطبع هل العدد فردي أم زوجي.
إذا قرأتم هذه الشيفرة فحسب، فهي سهلة القراءة جدًا، أليس كذلك؟ لديكم حلقة (Loop) تمر على الأعداد من 1 إلى 10، من دون تضمين 10، ثم لدي if is_even. هذا رائع. هنا أستخدم الدالة التي كتبتها في وسط قطعة أخرى من الشيفرة نوعًا ما.
وهذا جيد، لأنني، كما قلت قبل بضع شرائح، استدعاءات الدوال مجرد تعبيرات أساسًا. تُشغّل وتُقيَّم، وتحصلون منها على قيمة واحدة. ثم تحل تلك القيمة محل الاستدعاء.
هذا جيد إذن. لنستخدم نتيجة is_even، القيمة المعادة من طريقة is_even، داخل شرط (Conditional). إذا أعاد استدعاء is_even مع i القيمة True، فهذا يعني أنه إذا كان العدد زوجيًا، نطبع تلك القيمة، ثم فاصلة وeven. وإلا نطبع تلك القيمة، ثم فاصلة وodd.
هنا لا أعرّف دالة. لاحظوا أنها ليست مغلفة في def أو شيء كهذا. أستخدم دالة كتبتها مسبقًا فحسب. هنا في الداخل. سأحوّل هذا إلى تعليق فقط.
هذه الشيفرة التي كانت لدينا للتو على الشريحة. مرة أخرى، لاحظوا أنها ليست داخل— ليست مغلفة داخل دالة. إنها مجرد حلقة تخبرني بالأعداد واحدًا واحدًا، وهل هي فردية أم زوجية. فتطبع 1، ثم فاصلة— نعم؟
أحد الحضور: ماذا تفعلين عندما تحددين [غير مسموع] التعليق وتجعلينه مثل—
آنا بيل: آه، حين أحدد كل شيء. أستخدم فقط قدرة Spyder على— فأضغط Control 1، أو ربما Command 1 على Mac. وهي تضيف التعليقات وتزيلها دفعة واحدة. نعم، مفيدة جدًا. نعم.
وهذه الشيفرة الآن سهلة جدًا في التعديل. يمكنني اختيار 100 فحسب، ثم تشغيلها من جديد. وتعطيني الأعداد من 1 إلى المئة، فردية أو زوجية. ويمكنكم تخيل استخدام دالة is_even في سياق أكثر تعقيدًا.
وis_even دالة بسيطة جدًا في الكتابة، لكن مرة أخرى، يمكنكم تخيل كتابة دالة أكثر تعقيدًا. وعندئذ لا تكون تلك الدالة المعقدة قطعة كاملة من الشيفرة تُحشر في هذا البرنامج، في هذه الحلقة. ستكون دالة تستدعونها، وتستطيعون قراءة مواصفتها بسهولة، ولا تحتاجون إلى فهم كيف تعمل بالكامل لكي تستخدموها.
سنمر بمثال آخر لكتابة دالة صغيرة. وسيعرض هذا أيضًا أفضل الممارسات لكتابة الدالة والشيفرة، خصوصًا ربما في اختبار قصير أو نحو ذلك: كيف تكتبون الشيفرة تدريجيًا، وتختبرونها جزءًا جزءًا، وهكذا. المثال الأخير الذي أريد تناوله: أريد كتابة شيفرة تجمع كل الأعداد الصحيحة الفردية بين a وb، مع تضمينهما.
قد يكون هذا شيئًا يُطلب منكم في اختبار قصير. أول ما تفعلونه حين تواجهون مهمة كهذه هو التفكير في اسم مناسب للدالة. فـsum_odd أو sum_odds اسم معقول.
أما مدخلات الدالة، فلدي طرفان أريد جمع الأعداد الفردية بينهما. إذن قد يكون المدخلان هما طرفيّ المجال. ثم ما الشيء الذي تنجزه دالتك؟
صفحة المصدر 11
في النهاية ستعطيني مجموعًا ما. فلنسمّ هذا المجموع متغيرًا sum_of_odds، وسنعيده في نهاية الدالة. وبينهما ستكون لدينا بعض الشيفرة.
أول ما تفعلونه إذن هو ألا تكتبوا الشيفرة فورًا. حين تواجهون مهمة، مرة أخرى في اختبار قصير أو نحو ذلك، من الأفضل أخذ ورقة، وكتابة القليل، مثال واحد، ومحاولة التفكير في كيفية حلها، لا كما يفعل الإنسان. فنحن سنعرف المجموع فورًا. من السهل جدًا للبشر تحديد حلول هذه المسائل.
لكن حاولوا التفكير في كيفية كتابة— أي نوع من الوصفات سيعمل هنا؟ هل ستستخدمون حلقة؟ هل سيكون لديكم شرط؟ هل ستستخدمون حلقة for أم حلقة while، ومجموعة مفاهيم أخرى سنتعلمها في المحاضرات التالية؟ لكن الأمر الأساسي هو ألا تكتبوا الشيفرة فورًا.
إذا بدأنا بمثال بسيط جدًا على الورق، يمكننا القول: لنختر الطرفين a هو 2 وb هو 4. على الورق سأكتب غالبًا 2 و3 و4 في صف. فأعرف الأعداد التي علي النظر إليها. سأقول إن 2 هي a و4 هي b. أحتاج إلى النظر إلى كل عدد منها واحدًا واحدًا. معقول.
يمكنني عمل مثال آخر. آسفة. وأعرف ما ينبغي أن تكون الإجابة. أحدد ما ينبغي أن تكون الإجابة كي أعرف بالفعل ما أبحث عنه حين أكتب شيفرتي.
أنظر إلى مثال آخر، أكثر تعقيدًا قليلًا مثلًا، مجال أكبر: a هي 2 وb هي 7. أحاول استخدام الاستراتيجية نفسها، الوصفة نفسها التي استخدمتها لحل المثال الأبسط، في هذا الأصعب. مرة أخرى، سأكتب كل الأعداد بين 2 و7 مع تضمين الطرفين. هذا الأول، وهذا الأخير.
وكانت استراتيجيتي المرور عليها واحدًا واحدًا؛ إذا كان العدد فرديًا آخذه إلى مجموعي الجاري (Running sum)، وأضيفه إليه، وإذا كان زوجيًا فلا أفعل. أتجاهله. ومرة أخرى، أعرف أن الإجابة لهذا ينبغي أن تكون 50.
ملاحظة المترجم: ورد العدد
50في التفريغ الرسمي؛ مجموع الأعداد الفردية3 + 5 + 7في هذا المثال هو15. يُحفظ العدد المنقول من المصدر مع بيان التعارض، دون تقديمه بوصفه نتيجة حسابية صحيحة.
ومع هذين المثالين في الذهن، يمكنني بدء كتابة الشيفرة. لكن بدلًا من كتابة شيفرة للمسألة الكبيرة التي قد تتضمن تفاصيل دقيقة أو حالات حدية (Edge cases)، أستطيع محاولة حل مسألة مشابهة. بدلًا من جمع كل الأعداد الفردية بين a وb، لنجمع كل الأعداد بينهما، ونرى هل نستطيع جعل الشيفرة تعمل لهذا. وبعد ذلك ينبغي أن يكون تحديد الفردية منها تعديلًا صغيرًا لشيفرتنا.
إذا بدأنا بتحديد مجموع كل الأعداد الفردية بين a وb مع تضمينهما، فهذا يبدو كحلقة. لأنني عرفت، حين كتبت المثال على الورق، أن علي المرور بكل عدد بين a وb مع تضمينهما. فأعرف أنني أحتاج إلى حلقة تمر بكل قيمة من هذه القيم.
حلقة while أم for؟ الخيار لكم. سأستخدم كليهما في الشرائح، لنرى الشكل فحسب. باستخدام for يكون الأمر سهلًا: فقط for i in range(a, b). لكن باستخدام while، تذكروا أننا نحتاج إلى تهيئة متغير الحلقة إن كان لدينا واحد: i = a.
شرط الحلقة هو while i <= b. سنكرر بينما أنظر إلى كل هذه القيم حتى b مع تضمينها. وأحتاج إلى تذكر زيادة متغير الحلقة داخلها، بمقدار واحد كل مرة في هذه الحالة.
وماذا أفعل داخل الحلقة؟ سـ— تذكروا أننا نحل مسألة مشابهة. سأحتفظ بمجموع جارٍ. بمجرد رؤية i جديدة، سأضيفها إلى مجموعي.
صفحة المصدر 12
أدرك هنا أن بيئة التطوير (IDE) ستُظهر لي على الأرجح وجود خطأ. لم أهيئ sum_of_odds. فأتذكر تهيئته قبل الحلقة مباشرة. ثم هذا موضع جيد لاختبار الشيفرة قليلًا. سنختبرها بمثال بسيط جدًا، 2، فاصلة، 4.
إذا اختبرناها بـ2، فاصلة، 4، تعطيني حلقة for العدد 5، لكن حلقة while تعطيني 9. ربما لاحظتم المشكلة. حلقة for تمر حتى متغير النهاية، b، لكن من دون تضمينه.
يمكننا إضافة عبارة print إذا لم تحددوا ذلك، وستخبرنا عبارة الطباعة فعلًا. تخبرنا بما نزيده. أولًا 2، ثم 3، لكنني لا أصل إلى 4 أبدًا. فالإصلاح هو تغيير نهاية range فقط لتصبح زائد 1. ثم نشغّلها من جديد ونرى أن الإجابتين تتطابقان.
وهذا يحل المسألة الأكبر. كل ما نحتاج إليه الآن هو إضافة التفصيل الدقيق، الجزء الذي نأخذ فيه الأعداد الفردية فقط. ونقول هنا: إذا كنت آخذ الأعداد الفردية فقط، فأريد إضافة i إلى sum_of_odds حين أرى عددًا فرديًا فقط.
يمكنني هنا استخدام دالة is_even التي كتبتها مسبقًا. سأقول if not is_even، أو يمكنني فعل ذلك من جديد مباشرة. إذا كان i%2==1، نفعل هذا. والآن يمكننا تشغيلها من جديد، ونأمل أن تطابق المثال الذي كان لدي على الورق. وهي تطابقه بالفعل.
الفكرة هنا هي محاولة حل مسألة أبسط أولًا، ثم حين ترون مزيدًا من تفاصيل المسألة الدقيقة، تضيفون الوظائف شيئًا فشيئًا، كي لا تتعطلوا أمام مجموعة مشكلات كثيرة قد تظهر حين تكتبون قدرًا كبيرًا من الشيفرة. الخطوة الأخيرة اختبارها على المثال الآخر، للتأكد فقط من أنها ما زالت تعمل. فإذا طبعنا sum_of_odds بين 2 و7 من جديد، تطابق ما كتبته على الورق.
إذا لم تريدوا استخدام عبارات print، فإن Python Tutor أداة رائعة للتنقيح أيضًا. اختبار الشيفرة باستمرار مفيد جدًا. أظن أنني أكدت هذا في المحاضرات السابقة أيضًا. استخدام print أو Python Tutor للتنقيح مفيد جدًا أيضًا. لا أنوي بالفعل المرور بتمرين «جرّب بنفسك» هذا، لكنه، مع أمثلة أخرى كثيرة وأشياء تجربونها في المنزل، موجود في ملف Python.
من الدوال التي يمكنكم كتابتها is_palindrome وkeep_consonants وfirst_to_last_diff. اقرأوا مواصفة الدالة وحاولوا كتابة شيفرة تطابقها. وكالعادة، الإجابات في ملف Python، لكن من فضلكم، من فضلكم، حاولوا تنفيذها بأنفسكم أولًا قبل النظر إلى الإجابات.
ملخص سريع. الدوال مفيدة جدًا. تتيح لنا تجريد مهام مفيدة معينة. نجرد أساسًا وظائف قد نعيد استخدامها مرات كثيرة في برنامجنا.
تأخذ الدوال مدخلات، ولديها شيء تعيده. سنرى في المرة القادمة ما يحدث حين لا نعيد شيئًا. إنشاء الدالة يختلف عن تشغيل الدالة.
تنشئون الدالة مرة واحدة، لكن يمكنكم تشغيلها مرات كثيرة جدًا. وهذا ما يجعل الدوال مفيدة. تجعل الشيفرة سهلة الكتابة والقراءة والتنقيح والتعديل، وتؤدي إلى شيفرة جميلة ومتينة جدًا.