ملخص
يفتتح هذا الدرس الوحدة الخامسة من الدورة، وهي مخصّصة للدوال. ويشرح الغاية من الوحدة: أن تعلّم تفكيك المسألة إلى أجزاء ذات أسماء، لكلٍّ منها واجهة وعقد، وحلّ بعض المسائل بردّها إلى نسخة أصغر منها، أي بالاستدعاء الذاتي؛ ويقدّم دروسها الأربعة والخيط الذي يجمعها؛ ويذكّر بما تفترض الوحدة أنه معلوم، ولا سيما الحلقات ولامتغيراتها من الوحدة 4؛ ويصوغ المسألة التي تحلّها الوحدة، وهي مسألة برنامج يكرّر الحساب نفسه منسوخًا، فيجيب خطأً بسبب سهوٍ في النسخ دون أن يقول المترجم شيئًا؛ ويقدّم مسبقًا العقد (الشرط المسبق والشرط اللاحق) بوصفه الصيغة التي ستحدّد بها الوحدة مواصفات كل جزء؛ ويبيّن إلى أين تقود، في الوحدات اللاحقة وفي دورة البرمجة بلغة C++ II.
أهداف التعلم
- وصف تسلسل محتويات الوحدة وشرح ما يقدّمه كل درس للدرس الذي يليه.
- تذكّر خوارزميات الوحدة 4 (تتبّع الحلقة، ولامتغيرها ومتغايرها) ومراجعتها عند الحاجة.
- استباق فكرة العقد بمثال: ما يشترطه جزء من البرنامج على من يستعمله، وما يضمنه له في المقابل.
- التعرّف على ما يُنتظر إتقانه عند إنهاء الوحدة، وتمييزه ممّا سيُتعلَّم في الوحدات اللاحقة.
تقييم الدخول
قبل اجتياز الوحدة يحسن قياس نقطة البداية. وهذا التقييم تقييم ذاتي: لا تُمنح عليه درجة، وغايته الوحيدة توجيه الدراسة. وتتناول الأسئلة الخمسة الأولى ما تفترض الوحدة أنه معلوم: تتبّع الحلقة، ولامتغيرها، والشرط المسبق لمواصفات ما، ونسخ قيمة عند التهيئة، والبحث داخل برنامج من الوحدة 4 عن حساب له مدخلاته ومخرجاته الخاصة. وتتناول الأسئلة الخمسة التي تليها، بترتيب الدروس، الأفكار المحورية في الوحدة: مكدس الاستدعاءات، وتمرير قيمة إلى الدالة، وفصل الحساب عن العرض، والتعريف التراجعي للمضروب، وتنفيذ دالة تستدعي نفسها. ولا أحد ملزم بالإصابة فيها بعدُ، وفي بعضها يكون «لا أعرف بعدُ» جوابًا صادقًا: فكل جواب، صائبًا كان أو خاطئًا، يشرح المسألة ويسمّي الدرس الذي يعالجها. وتُقاس المهارات نفسها مرة أخرى في النهاية، في درس الخلاصة.
قبل أن تبدأ الوحدة، قِس نقطة انطلاقك. إنه تقييم ذاتي قصير: لا يُمنح عليه درجة، وتُحفظ نتيجته في متصفحك فقط.
السؤال 1من وحدات سابقةماذا يكتب
int n{305}; int c{1}; while (n >= 10) { n = n / 10; ++c; } std::println("{}", c);؟يعدّ الجولات وينسى أنcيبدأ من 1.يزيد جولة: مع يكون الشرطn >= 10خاطئًا من قبل.يخلط بين العدّاد وقيمةnبعد الجولة الأولى.يعطي التتبّع و: يُنفَّذ الجسم مرتين، ومع يخفق الشرط. فالحلقة تعدّ أرقام عدد طبيعي؛ ودرس الدوال يضعها في دالة ذات اسم وعقد، ودرس الاستدعاء الذاتي يكتبها دون حلقة.راجِع: الحلقات
السؤال 2من وحدات سابقةفي الحلقة
int total{0}; int k{1}; while (k <= n) { total += k; ++k; }، مع ، ما العلاقة التي تصحّ في كل مرة يُحسب فيها الشرط؟تصحّ في منتصف الجولة، بعد الجمع وقبل الزيادة، لا عند حساب الشرط.تصحّ عند الانتهاء فقط: إنها الشرط اللاحق، لا اللامتغير.إنه شرط الحلقة، وهو خاطئ عند الخروج تحديدًا؛ واللامتغير يصحّ حينئذ أيضًا.تصحّ في البداية، مع المجموع الفارغ و؛ وإن صحّت قبل جولةٍ صحّت بعدها، لأن الجولة تضيف ثم تزيده. وعند الخروج يكون ، فيعطي اللامتغير المجموع من 1 إلى . ودرس الاستدعاء الذاتي يسوّغ دواله بالاستدلال نفسه بالاستقراء.راجِع: تصميم حلقات صحيحة
السؤال 3من وحدات سابقةتقول مواصفات القسمة: «إذا أُعطي عددان صحيحان و، فالمطلوب إيجاد و بحيث و». ما شرطها المسبق؟
يخلط بين الشرط المسبق والشرط اللاحق: هذا ما يحقّقه الناتج، لا ما يُشترط في البيانات.هذا شرط على المخرَج، لا على المدخَل.مع لا يوجد أي يحقّق : فلا حل للمسألة، والشرط المسبق يستبعد ذلك.الشرط المسبق هو ما يُشترط في البيانات كي يكون للمسألة حل؛ والشرط اللاحق ما يحقّقه الناتج. والوحدة تحوّل هذا الزوج إلى عقد كل دالة: الشرط المسبق يُلزم من يستدعي، والشرط اللاحق يُلزم الدالة.راجِع: من المسألة إلى الخوارزمية
السؤال 4من وحدات سابقةبعد
int a{3}; int b{a}; b = 7;، كم تساويa؟يظن أنbوaتبقيان مرتبطتين إلى الأبد؛ والتهيئة تنسخ القيمة لا الخانة.يخلط بين النسخ ومتغير غير مهيَّأ: فقد تلقّىaالقيمة 3 ولم يغيّرها شيء.التهيئةint b{a}تنسخ في خانةbالقيمة التي كانت لـa؛ وبعد ذلك تصبح الخانتان مستقلتين. ودرس الوسائط ونطاق الرؤية يبيّن أن الوسيط يُهيَّأ بالطريقة نفسها، بنسخة من القيمة الممرَّرة، ويعلّم الإمكان الآخر: اسمًا ثانيًا للخانة نفسها.راجِع: المتغيرات والذاكرة
السؤال 5من وحدات سابقةالبرنامج الذي يكتب الأعداد الأولية الأصغر من 100 في الدرس 4.4 فيه حلقة تمرّ على المرشّحين، وداخلها حلقة أخرى تختبر قواسمهم. أيّ جزء منه حساب له مدخلاته ومخرجاته الخاصة، يحسن أن يُعطى اسمًا؟
يخلط بين مدخلات البرنامج كله ومخرجاته ومدخلات حساب داخلي ومخرجاته.إنها تعليمة، لا حساب ذو معنى خاص: ليس لها مدخل وناتج يريد أحد أن يستعملهما.يخلط بين معطى ومهمة: فالمتغير يحفظ ناتج الحساب، لكنه ليس الحساب.الحلقة الداخلية لا تتوقف إلا على المرشّح، وناتجها نعم أو لا. وإعطاؤها اسمًا يتيح قراءة الحلقة الخارجية بوصفها مواصفاته: «لكل مرشّح، إن كان أوليًّا فاكتبه». ودرس التصميم بالدوال يعيد كتابة ذلك البرنامج على هذا النحو.راجِع: الخوارزميات العددية
السؤال 6يبدأ برنامج في
main؛ وتستدعيmainدالةg، وتستدعيgدالة أخرىh. ما الاستدعاءات الجارية أثناء تنفيذh؟يظن أن من يستدعي يختفي عند الاستدعاء؛ والواقع أنه ينتظر ليتابع من حيث توقّف.يظن أنgانتهت عندما استدعتh؛ وهي ستنتهي عندما تعيد إليهاhالتحكم.هذا جواب صادق: الدرس 5.1 يشرحه برسم للمكدس.السؤال 7مع الدالة
int f(int x) { x = x + 1; return x; }، ماذا يكتبint a{3}; int b{f(a)}; std::println("{} {}", a, b);؟يظن أن الدالة تعدّل القيمة الممرَّرة؛ وهي افتراضيًّا تتلقّى نسخة.يظن أن تغييرxلا يبلغ حتى القيمة المُعادة.يظن أن الوسيط لا يمكن تعديله؛ والواقع أنه يمكن، ما لم يُصرَّح بهconst.الوسيطxمتغير من متغيراتfيُهيَّأ بنسخة من القيمة الممرَّرة: فالدالة تغيّر النسخة وتعيد 4، ويحتفظaبقيمته 3. إنه التمرير بالقيمة، وهو الطريقة الافتراضية في C++.راجِع: الوسائط ونطاق الرؤية
السؤال 8يقرأ برنامج تاريخًا وعليه أن يكتب يوم الأسبوع الذي يوافقه. أيّ تفكيك يفصل الحساب عن العرض؟
تخلط مهمتين: فما حُسب لا يمكن إعادة استعماله ولا مقارنته بقيمة متوقَّعة.يفصل المهام، لكنه يصل بينها بأثر خفي: فمن يقرأ الاستدعاء لا يرى من أين يأتي المعطى.هذا جواب صادق: الدرس 5.3 يعلّم كيف نقرّر أيّ الدوال نكتب.هكذا يُختبر الحساب بالأعداد، دون النظر إلى الشاشة، ويمكن أن يخدم برنامجًا آخر يكتب الناتج بطريقة أخرى. والدرس 5.3 يسمّي هذا المعيار «دالة واحدة، مهمة واحدة».راجِع: التصميم بالدوال
السؤال 9أيّ التعريفات الآتية يعرّف لكل عدد طبيعي ؟
من دون حالة أساسية لا يتوقف التعريف أبدًا: يحيل إلى ، وهكذا بلا نهاية.يعرّف تعريفًا صحيحًا عندما ، لكنه يترك بلا تعريف، وقيمته 1.يعرّف بدلالة نفسه: التعريف دائري ولا يصغّر المسألة.حالة أساسية لا تتوقف على شيء، وحالة تراجعية تردّ إلى : لحساب ننزل حتى ونضرب عند الرجوع. ودرس الاستدعاء الذاتي يترجم هذا التعريف إلى دالة تستدعي نفسها.راجِع: الاستدعاء الذاتي
السؤال 10مع
int f(int n) { if (n == 0) { return 0; } return n + f(n - 1); }، ماذا يكتبstd::println("{}", f(3));؟يظن أنه لا يُنفَّذ إلا استدعاء واحد، أو يخلط بين القيمة الممرَّرة والناتج.يظن أنreturn 0في الحالة الأساسية هو ناتج السلسلة كلها؛ وهو ناتج الاستدعاء الأخير فقط.اسم الدالة معروف منذ ترويستها، ويمكن استعماله في جسمها نفسه.f(3)تنتظرf(2)، التي تنتظرf(1)، التي تنتظرf(0)؛ وهذه تعيد 0، وعند الرجوع تُجمع 1 و2 و3. فالدالة تحسب ، وهو المجموع نفسه الذي تحسبه حلقة السؤال الثاني.راجِع: الاستدعاء الذاتي
جارٍ تحميل المحتوى…