بسم الله الرحمن الرحيم
احب ان اشير هنا أن كل ما تعلمنا وما سوف نتعلمة من عبارات التحكم ( أو قطع بناء البرنامج ) هو من أجل توجيه الكائن للقيام بعمل ( أو اعمال )
محدد. نحن ندرس أفكار وتقنيات , وفي النهاية نغلفها ( نكبسلها ) على شكل اصناف ( classes ) لكي نواكب البرمجة الحديثة التي تعطينا الجودة
والأداء.
في هذه المحاضرة سوف نستأنف مواضيعنا في البرمجة الهيكلية بشرح عبارات التحكم المتبقية وهي for , do .. while , switch
وتلك العبارات جميلة وممتعه واستخدامها يوفر لنا حلول غير منتهية وأفكار تغذي جميع اتجاهات البرمجة.
في نهاية المحاضرة سوف ننتهي من الفصل الخامس وسوف نتناول المواضيع التالية :
1- أساسيات التكرار المحدد.
2- عبارة التكرار for.
3- أمثلة على استخدام العبارة for.
4- عبارة التكرار do .. while.
5- عبارة الإختيار المتعددة switch.
6- عبارة break وعبارة continue.
7- العمليات المنطقية.
8- مالذي يحدث عند الخلط بين المقارنة ( == ) وعملية الإسناد ( = ) ؟
بسم الله وبه نستعين.
أساسيات تكرار العداد المحدد
قبل الشروع في شرح العبارات الثلاث المتبقية , نرجع لعبارة الـ while ونوضح بعض
الأمور الأساسية في أي تكرار محدد بعداد ( متغير تحكم ). نأخذ الكود الصغير التالي :
الكود 5.1
1 // Fig. 5.1: fig05_01.cpp
2 // Counter-controlled repetition.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int counter = 1; // declare and initialize control variable
10
11 while ( counter <= 10 ) // loop-continuation condition
12 {
13 cout << counter << " ";
14 counter++; // increment control variable by 1
15 } // end while
16
17 cout << endl; // output a newline
18 return 0; // successful termination
19 } // end mainهذا الكود يقوم بطباعة الأعداد من 1 إلى 10 على سطر واحد.
كما تلاحظون قمنا بالإعلان عن متغير التحكم في السطر 9 وفي نفس الوقت أعطيناه قيمة أولية وهي الـ 1. في السطر 14 تلاحظون
أن مقدار الزيادة هي +1 في كل مرة , وبدايتها تكون بعد أول مقارنة, وفي مرة نقارن الشرط ( سطر 11 ) سوف نقوم بالزيادة بعدها
إلى أن يصبح متغير التحكم قد فاق الـ 10 وعندها سوف يخرج من التكرار لأنه نافى الشرط.
يمكن أن نقصر من طول الكود ونجعله اكثر وضوح لو قمنا بإعطاء قيمة اولية لمتغير التحكم هي الصفر بدلا من الواحد , وتصبح الشرط
كالتالي :
while ( ++counter <= 10 ) cout << counter << " ";
وبهذه الطريقة استغنينا عن الأقواس وبسطنا البرنامج واستغنينا عن
الكثافة الزائدة.
إذن نخلص من هذا كله أن تكرار العداد المحدد يتطلب الآتي :
1- اسم متغير التحكم ( عداد التكرار ).
2- قيمة أولية لمتغير التحكم.
3- شرط استمرارية حلقة التكرار الذي يختبر القيمة النهائية لمتغير التحكم.
4- المزودة أو المنقصة لتحريك عجلة التكرار .
خطأ برمجي شائع : تعريف متغير التحكم من نوع float أو double بحيث من الممكن أن يحتوي على فواصل عشرية تؤدي إلى اختبارات
غير دقيقة.
لممارسة البرمجة بشكل جيد : لعدم تعقيد البرنامج احرص على عدد زيادة حلقات التكرار عن ثلاثة مستويات , واحرص على الفراغات الأفقية
والعمودية اثناء كل حلقة
عبارة التكرار for
بعد توضيح اساسيات التكرار المحدد في الموضوع السابق , يمكن إعادة كتابة الكود السابق لتوضيح فكرة عبارة التكرار for كالتالي :
الكود 5.2
1 // Fig. 5.2: fig05_02.cpp
2 // Counter-controlled repetition with the for statement.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 // for statement header includes initialization,
10 // loop-continuation condition and increment.
11 for ( int counter = 1; counter <= 10; counter++ )
12 cout << counter << " ";
13
14 cout << endl; // output a newline
15 return 0; // indicate successful termination
16 } // end mainيبدأ التنفيذ لعبارة الـ for ( السطور 11- 12 ) حيث يتم الإعلان عن متغير التحكم خلال العبارة وأيضا اعطائه قيمة اولية , بعدها يأتي
شرط استمرارية التكرار counter <= 10 حيث يتم اختباره. اذا تحقق الشرط سوف يتم طباعة قيمة العداد ( السطر 12 ).
التالي هو زيادة القيمة counter بواحد ( وذلك في عبارة التكرار نفسها ) , عندما سوف يكون قيمة متغير التحكم تساوي 2 ومن ثم
يتم فصح الشرط , وهكذا إلى أن تصبح قيمة متغير التكرار 11 , بعدها ينتفي الشرط ويخرج التحكم من سيطرة العبارة لينتقل للسطر 14.
مكونات سطر التعريف للعبارة for ( ترويسة العبارة ):
في الشكل السابق , هذا كل مافي هذه العبارة ( كل ما يحتاجة العداد ).
تخيل أننا نيسينا وضع علامة التساوي في الشرط ليصبح بهذا الشكل counter < 10 . بذلك سوف يتم تنفيذ العبارة تسع مرات فقط
ويخرج التحكم من سيطرة العبارة عندما يحتوي العداد القيمة 10 . اذا نتج هذا عن خطأ ( نسيان مثلا ) فإن هذا الخطأ يسمى ( off_by_one error ).
وهو خطأ شائع في أوساط المبرمجين الجدد.
لترسيخ شكل عبارة التكرار for , يمكن كتابة الشكل العام لها كالتالي :
( عبارة الزيادة ; شرط استمرارية التكرار ; القيمة الأولية للعداد ) for
يمكن أن نمثل عبارة الـ for بعبارة الـ while كالتالي :
( شرط استمرارية التكرار ) while
}
;العبارة المراد تنفيذها
; علمية الزيادة
{
التمثيل السابق له استثناء , وذلك عند استخدام عبارة الـ continue , وسوف نتناول ذلك في ذلك الموضوع بعد قليل.
خطأ برمجي شائع : إذا تم الإعلان عن متغير التحكم داخل عبارة الـ for فإنه من الخطأ استخدامة خارج جسم الـ for.
عملية الفاصلة المنقوطة في عبارة الـ for تفصل قائمة التعابير فيها , بحيث يكون كل تعبير مستقل. وكل قسم يمكن ان يحتوي
عدة متغيرات , بدلا من متغير واحد ( كما في مثالنا السابق ).
نعم , نستطيع ترك عبارة الـ for خالية تماما ( يكون مابين القوسين فاضي ) , حيث يعتبرة المترجم صحيح , ويمكن ايضا التعبير
عن عداد التكرار ( متغير التحكم ) بأي من الأشكال التالي :
counter = counter + 1
counter += 1
++counter
counter++
جيمع التعبيرات السابقة لها نفس العمل.
خطأ برمجي شائع : وضع الفاصلة بدل الفاصلة المنقوطة يولد خطا نصي ووضع فاصلة منقوطة بعد العبارة مباشرة يجعل العبارة
فارغة ويفصل جسم التكرار عن عبارة التكرار وينتج خطأ منطقي يؤدي إلى نتائج خاطئة.
فكرة مفيدة في هندسة البرمجيات : وضع فاصلة منقوطة بعد عبارة الفور يستخدم دائما المبرمجين لكي يأخرو طور النتائج على الشاشة
لكي يمكن المستخدم من القراءة. ومدة التأخير تختلف من نظام لآخر حسب سرعة كل نظام.
القيمة الأولي , وشرط استمرارية التكرار وتعبير الزيادة , يمكن كل تلك التعبيرات ان تحتوي على عبارات حسابية , لنأخذ مثالا على ذلك :
اذا افترضنا التالي , x= 2 وَ y= 10 وَ x,y لا يتم عليها التعديل خلال جسم التكرار. فإن :
( for ( int j=x ; j <=4*x*y;j+=y/x
تساوي
( for ( int j=2; j<=80;j+=5
ليس شرطا أن تكون العملية دائما في تسيير عجلة التكرار أن تكون عملية الجمع , بل يمكن استخدام عملية الطرح .
تمثيل مخطط الحركة للعبارة for بالـ UML:
يمكنك مقارنته بالـ UML لعبارة التكرار while واستنتاج الفوارق.
أمثلة على استخدام عبارة الـ for
نعرض فيما يلي طرق مختلفة للتعبير عن متغير التحكم , وأشكال استخدامة , لاحظ التعبيرات الحسابية في كل ترويسة .
( أ ) يتفاوت متغير التحكم من 1 إلى 100 بزيادة واحدة كل مرة.
for ( int i= 1 ; i<= 100 ; i++ )
( ب ) يتفاوت من 100 إلى 1 في كل مرة يزيد بـ -1 ( تقنيص 1 )
for ( int i = 100 ; i>= 1 ; i-- )
( ج ) يتفاوت من 7 إلى 77 , في كل خطوة ننقص بمقدار 7 .
for ( int i = 7 i <= 77 ; i+= 7 )
( د ) من 20 إلى 2 ( في كل خطوة ننقص بمقدار -2 )
for ( int i = 20 ; i>= 2 ; i-=2 )
( ه ) يتفاوت خلال سلسلة من القيم وهي 2و 17 و 14 و 11 :
for ( int i = 2 ; i<= 20 ; i+=3 )
( و ) يتفاوت خلال السلسلة 0و 11 و 22 و 44 ... :
for ( int i= 99; i>=0 ; i-=11 )
مثال تطبيقي ( جمع الأعداد الزوجية من 2 إلى 20 ) :
سوف نأخذ مثالين نطبق فيهما عبارة الـ for , المثال التالي يتم جمع الأعداد الموجبة من 2 إلى 20
الكود 5.5
1 // Fig. 5.5: fig05_05.cpp
2 // Summing integers with the for statement.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int total = 0; // initialize total
10
11 // total even integers from 2 through 20
12 for ( int number = 2; number <= 20; number += 2 )
13 total += number;
14
15 cout << "Sum is " << total << endl; // display results
16 return 0; // successful termination
17 } // end mainوسوف تؤدي نفس الغرض
لممارسة البرمجة بشكل جيد : فكرة الدمج السابقة بالرغم من صحتها إلى أنها تصعب قراءة البرنامج وصيانته وتطويره. لذلك ينصح دائما أن
نجعل الترويسة سطر واحد فقط.
تطبيق حسابات الربح المركبه :
المثال التالي يحسب حسابات الربح المركبه بإستخدام عبارة فور , اعتبر أن لدينا المشكلة التلاي :
شخص ربح 100 ريال في حساب يغطي 5% فائدة . افترض أن كل الفائدة حسبت على المبلغ المودع في الحساب, احسب واطبع مقدار النقود في الحساب عند نهاية كل
سنة لعشر سنين. استخدم المعادلة التالية لتحديد هذا المقدار :
a = p ( 1 + r ) n حيث :
p : المقدار الأصل المستفاد ( مثلا الاساسي ).
r : معدل الربح السنوي.
n : عدد السنين.
a : المبلغ في التوديع عند نهاية السنة المعنية.
هذه المهمة تكلف حلقة التكرار أن تؤدي عملية الحساب المشار إليها لكل 10 سنين على النقود المتبقية في الإيداع . الكود كالتالي :
كود5.6
1 // Fig. 5.6: fig05_06.cpp
2 // Compound interest calculations with for.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6 using std::fixed;
7
8 #include <iomanip>
9 using std::setw; // enables program to set a field width
10 using std::setprecision;
11
12 #include <cmath> // standard C++ math library
13 using std::pow; // enables program to use function pow
14
15 int main()
16 {
17 double amount; // amount on deposit at end of each year
18 double principal = 1000.0; // initial amount before interest
19 double rate = .05; // interest rate
20
21 // display headers
22 cout << "Year" << setw( 21 ) << "Amount on deposit" << endl;
23
24 // set floating-point number format
25 cout << fixed << setprecision( 2 );
26
27 // calculate amount on deposit for each of ten years
28 for ( int year = 1; year <= 10; year++ )
29 {
30 // calculate new amount for specified year
31 amount = principal * pow( 1.0 + rate, year );
32
33 // display the year and the amount
34 cout << setw( 4 ) << year << setw( 21 ) << amount << endl;
35 } // end for
36
37 return 0; // indicate successful termination
38 } // end mainعبارة الـ for ( السطر 28-53 ) تنفذ جسمها 10 مرات , يختلف متغير التحكم من 1 إلى 10 في زيادة مقدارها 1.
المكتبة القياسية لا تحتوي على عملية الأس , لذلك استخدمنا مكتبة اخرى تمكننا ( المكتبة < cmath > ) من استخدام الدالة pow على
الشكل التالي ( pow ( x, y والتي تتمثل في التعبير التالي المستخدم في البرنامج ( pow ( 1.0 + rate, year حيث المتغير rate يمثل
الـ r ومتغير السنة يمثل الـ n وهما قيمتين من نوع double.
خطأ برمجي شائع : نسيات إلحاق ملفات الترويسة المناسبة للبرنامج ولد خطأ عند الترجمة.
تحذير حول استخدام النوع double للمبالغ المالية :
لاحظ ذلك أن السطور( 17-19 ) تعلن عن المتغير amount و principal و rate على أهم أعداد حقيقية فعندما نقوم بجمع عددين
مثل 18.673 و 14.234 ليعطو العدد 32.967 والمطبوع 32.91 فهذا العدد كما تلاحظون غير دقيق في الإستخدامات المالي.
استخدام معاملات سيل الإخراج لتشكيل الإخراج الرقمي :
برنامجنا السابق يخرج لنا المخرجات التالية
Year Amount on deposit 1 1050.00 2 1102.50 3 1157.63 4 1215.51 5 1276.28 6 1340.10 7 1407.10 8 1477.46 9 1551.33 10 1628.89
كما تلاحظون لدينا دالة جديدة وهي set ( 4 ) السطور 22و 34 حيث تقوم بتنسيق العدد بمحاذاته لأقصى اليمين.
من المخرجات أيضا , تلاحظون استخدامنا لعبارة التنسيق في السطر 25 قبل حلقة التكرار يؤدي إلى تطبيق هذه العبارة على جميع
المخرجات , وهذا يسمى تثبيت الإعداد.
فكرة مفيدة في الأداء : ك تجنب وضع تعبير ثابت مثل الذي بالسطر 31 ( rate + 1.0 ) حيث ينفذ في كلة مرة نفس عملية
الجمع. ومن الأفضل وصفة مرة واحدة خارج التكرار.
عبارة التكرار do ... while
تختلف هذه العبارة عن العبارة while بترتيب تنفيذ جسمها, ففي هذه العبارة يتم تنفيذ جسم العبارة أولا ومن ثم اجراء المقارنة
بعكس عملية الـ while التي تتم فيها عملية المقارنة أولا ثم تنفيذ جسم التكرار.
إذا عبارة الـ do .. while يتم تنفيذ جسمها مرة واحدة على الأقل , بعكس الآخرى التي من الممن ألا ينفذ جسمها ولا مرة.
الشكل العام لهذه العبارة كالتالي :
do { statemetn } while ( condtion )الأقواس هنا يمكن الإستغناء عنها إذا كانت تحتوي عبارة واحدة فقط , ولكن يفضل دائما وضع الأقواس بغية اجتناب التشويش.
تطبيق على ذلك :
كود 5.7
1 // Fig. 5.7: fig05_07.cpp
2 // do...while repetition statement.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int counter = 1; // initialize counter
10
11 do
12 {
13 cout << counter << " "; // display counter
14 counter++; // increment counter
15 } while ( counter <= 10 ); // end do...while
16
17 cout << endl; // output a newline
18 return 0; // indicate successful termination
19 } // end mainالـ UML لمخطط حركة عبارdo .. while :
يمكن مقارنة بالـ UML للـ while.
عبارة الأختيار المتعددة switch
بالإضافة إلى عبارة if المفردة و if .. else المزدوجة , السي++ تعطي عبارة اختيار اخرى , تسمى عبارة الإختيار المتعدد switch.
هذه العبارة تختبر عدة شروط وتقوم بتنفيذ عدة اجراءات بناءً على نتيجة تحقق الشرط ( متغير أو تعبير ). كل شرط يسمى حالة , ويسمى السطر
( :case expression ) شريط.
صنف GradeBook مع العبارة switch لحساب درجات كل معدل :
في المثال التالي , سوف نعرض لكم نسخة محسنة من الصنف GradeBook الذي تم طرحه في الفصل الثالث والرابع. النسخة الجديدة من
الصنف تطلب من المستخدم أن يدخل مجموعة حروف ( تمثل تقديرات - معدلات - الطلاب ) وتعرض عدد الطلاب لكل معدل. الصنف يحدد
ما أذا كان التقدير المدخل هو الـ A أو الـ B أو الـ C أو الـ D أو الـ F. حيث يقوم بزيادة العداد بواحد كل مرة لكل تقدير اذا تحققت
احد الحالات ( الشروط ) في عبارة الـ switch.
الصنف معرف في الكود 5.9 , حيث يحتوي هذا الكود على توصيفات أولية لدوال الأعضاء تظهر تعاريفها في الكود 5.10. والكود 5.11 يقوم بتشغيل
بتعريف كائن وتشغيل واستعراض امكاناته.
الكود 5.9
1 // Fig. 5.9: GradeBook.h
2 // Definition of class GradeBook that counts A, B, C, D and F grades.
3 // Member functions are defined in GradeBook.cpp
4
5 #include <string> // program uses C++ standard string class
6 using std::string;
7
8 // GradeBook class definition
9 class GradeBook
10 {
11 public:
12 GradeBook( string ); // constructor initializes course name
13 void setCourseName( string ); // function to set the course name
14 string getCourseName(); // function to retrieve the course name
15 void displayMessage(); // display a welcome message
16 void inputGrades(); // input arbitrary number of grades from user
17 void displayGradeReport(); // display a report based on the grades
18 private:
19 string courseName; // course name for this GradeBook
20 int aCount; // count of A grades
21 int bCount; // count of B grades
22 int cCount; // count of C grades
23 int dCount; // count of D grades
24 int fCount; // count of F grades
25 }; // end class GradeBookالكود 5.10
1 // Fig. 5.10: GradeBook.cpp
2 // Member-function definitions for class GradeBook that
3 // uses a switch statement to count A, B, C, D and F grades.
4 #include <iostream>
5 using std::cout;
6 using std::cin;
7 using std::endl;
8
9 #include "GradeBook.h" // include definition of class GradeBook
10
11 // constructor initializes courseName with string supplied as argument;
12 // initializes counter data members to 0
13 GradeBook::GradeBook( string name )
14 {
15 setCourseName( name ); // validate and store courseName
16 aCount = 0; // initialize count of A grades to 0
17 bCount = 0; // initialize count of B grades to 0
18 cCount = 0; // initialize count of C grades to 0
19 dCount = 0; // initialize count of D grades to 0
20 fCount = 0; // initialize count of F grades to 0
21 } // end GradeBook constructor
22
23 // function to set the course name; limits name to 25 or fewer characters
24 void GradeBook::setCourseName( string name )
25 {
26 if ( name.length() <= 25 ) // if name has 25 or fewer characters
27 courseName = name; // store the course name in the object
28 else // if name is longer than 25 characters
29 { // set courseName to first 25 characters of parameter name
30 courseName = name.substr( 0, 25 ); // select first 25 characters
31 cout << "Name \"" << name << "\" exceeds maximum length (25).\n"
32 << "Limiting courseName to first 25 characters.\n" << endl;
33 } // end if...else
34 } // end function setCourseName
35
36 // function to retrieve the course name
37 string GradeBook::getCourseName()
38 {
39 return courseName;
40 } // end function getCourseName
41
42 // display a welcome message to the GradeBook user
43 void GradeBook::displayMessage()
44 {
45 // this statement calls getCourseName to get the
46 // name of the course this GradeBook represents
47 cout << "Welcome to the grade book for\n" << getCourseName() << "!\n"
48 << endl;
49 } // end function displayMessage
50
51 // input arbitrary number of grades from user; update grade counter
52 void GradeBook::inputGrades()
53 {
54 int grade; // grade entered by user
55
56 cout << "Enter the letter grades." << endl
57 << "Enter the EOF character to end input." << endl;
58
59 // loop until user types end-of-file key sequence
60 while ( ( grade = cin.get() ) != EOF )
61 {
62 // determine which grade was entered
63 switch ( grade ) // switch statement nested in while
64 {
65 case 'A': // grade was uppercase A
66 case 'a': // or lowercase a
67 aCount++; // increment aCount
68 break; // necessary to exit switch
69
70 case 'B': // grade was uppercase B
71 case 'b': // or lowercase b
72 bCount++; // increment bCount
73 break; // exit switch
74
75 case 'C': // grade was uppercase C
76 case 'c': // or lowercase c
77 cCount++; // increment cCount
78 break; // exit switch
79
80 case 'D': // grade was uppercase D
81 case 'd': // or lowercase d
82 dCount++; // increment dCount
83 break; // exit switch
84
85 case 'F': // grade was uppercase F
86 case 'f': // or lowercase f
87 fCount++; // increment fCount
88 break; // exit switch
89
90 case '\n': // ignore newlines,
91 case '\t': // tabs,
92 case ' ': // and spaces in input
93 break; // exit switch
94
95 default: // catch all other characters
96 cout << "Incorrect letter grade entered."
97 << " Enter a new grade." << endl;
98 break; // optional; will exit switch anyway
99 } // end switch
100 } // end while
101 } // end function inputGrades
102
103 // display a report based on the grades entered by user
104 void GradeBook::displayGradeReport()
105 {
106 // output summary of results
107 cout << "\n\nNumber of students who received each letter grade:"
108 << "\nA: " << aCount // display number of A grades
109 << "\nB: " << bCount // display number of B grades
110 << "\nC: " << cCount // display number of C grades
111 << "\nD: " << dCount // display number of D grades
112 << "\nF: " << fCount // display number of F grades
113 << endl;
114 } // end function displayGradeReportالكود 5.11
1 // Fig. 5.11: fig05_11.cpp
2 // Create GradeBook object, input grades and display grade report.
3
4 #include "GradeBook.h" // include definition of class GradeBook
5
6 int main()
7 {
8 // create GradeBook object
9 GradeBook myGradeBook( "CS101 C++ Programming" );
10
11 myGradeBook.displayMessage(); // display welcome message
12 myGradeBook.inputGrades(); // read grades from user
13 myGradeBook.displayGradeReport(); // display report based on grades
14 return 0; // indicate successful termination
15 } // end mainكما تشاهدون في الكود5.9 , حيث يحتوي الصنف على توصيفات أولية لدوال الأعضاء . في السطر 13 نشاهد دالة الأعضاء setCourseName
وفي السطر 14 نشاهد الـ getCouseName وفي السطر 15 نشاهد displayMessage وبقية القائمة بالإضافة إلى الباني.
أما في قسم الأعضاء فنشاهد خمسة أعضاء بيانات محددة الوصول.
الجديد في أعضاء البيانات يوجد في السطور 20 إلى 24, وهي متغيرات تمثل عدادات لكل تقدير.
اما الجديد في دوال الأعضاء هما الدالتين في السطرين 16 و 17 من الكود 5.9.
في دالة العضو inputGrades ( الكود 5.10 السطور 51-101 ) يتم قراءة عدد محدد من الحروف تمثل معدلات درجات الطلاب
هذه القراءة تتم ضمن حلقة التكرار while المحدده بحارس يتم إدخالة من قبل المستخدم.
في دالة العضو displayGradeReport ( الكود 5.10 السطرو 104-114 ) يتم عرض عدد الطلاب لكل تقدير.
في دالة العضو inputGrades , في كل دورة من دورات الـ while يتم ادخال حرف وبعد ذلك يتم اختبارة في بقية الأسطر.
نحن نعرف أن القيمة هي حرف , بالتالي هي عبارة عن رمز character, ومن خلال هاتين المعلومتين نعرف ان نوع المدخل هو
char.
لاحظو معي في السطر 60 , يتم أولا تنفيذ ()cin.get ومن ثم إسنادها إلى grade ومن ثم المقارنة بـ EOF.
الـ ()cin.get هي دالة عضو في الكائن cin الموجود ضمن المكتبة القياسية < iostream > وتستخدم للحصول على
رمز معين من المستخدم عن طريق لوحة المفاتيح. واستخدمناها هنا للحصول على الحرف الذي يمثل تقدير الدرجة.
نحن أعلنا عن المتغير grade في السطر 54 على أنه عدد صحيح , ثم قمنا بإسناد حرف إليه .. كيف ؟!!
الجواب هو , أن الحرف يمكن تمثيله بقيمة عدد صحيح int , ويمكن تمثيله برمز char, على سبيل المثال, العبارة التالية :
cout << "The character (" << 'a' << ") has the value "<BR> << static_cast< int > ( 'a' ) << endl;
تطبع الرمز وقيمته الصحيحة كالتالي :
The character (a) has the value 97
إذا , العدد 97 تمثيل للرمز 'a' في الكمبيوتر. معظم الكمبيوترات اليوم تستخدم مجموعة
الرموز ( ASCII ( Amercan StandArd Code for Information Interchange - الكود القياسي الأمريكي
لتبادل المعلومات - الذي فيه تمثيلنا هذا ( 9 = ' a ' ) , قائمة الرموز وتمثيلاتهم الرقمية يمكن تمثيلهم وفق نظام
الـ ASCII في الجدول التالي :
في دالة الـ inputGrades نحن قلنا بإننا سوف نقوم بإدخال عدد محدد من الرموز , ضمن حلقة تكرار محدد بالحارس. إذن , أين هو الحارس وكيف ؟
الحارس هو EOF وهو اختصار للجملة ( End Off File ) أي نهاية الملف. إذن كيف نقوم بإدخال هذه الحروف , هل ندخل E ثم O ثم F ؟
الجواب هو لا , ليس من هذا كله , حيث هذا الرمز معرف في كل نظام بشكل مختلف عن النظام الآخر , بحيث يمكن تمثيله في الويندوز بالإختصار ctrl+z
أما في اللينكس واليونيكس ctrl+d وهو عبارة عن عدد رمزي ثابت معرف في الترويسة < iostream >. وعند الضغط على هذا الإختصار سوف يتمثل
في على الشاشة z^ ( في نظام ويندوز ).
خلاصة ما تقدم , يقوم المستخدم بإدخال حروف الدرجات من لوحة المفاتيح , حرف واحد كل مرة , حيث يقرأ الحرف من لوحة المفاتيح عن طريق دالة العضو
()cin.get ومن ثم الضغط على مفتاح enter حيث يختبر هل الرمز يساوي EOF وعندما يتحقق الشرط ندخل في عبارة الـ switch ( السطور 63-99)
حيث يتم زيادة عداد كل درجة , وعندما نريد أن ننهي سلسلة ادخال حروف التقدير سوف نضغط على ctrl+z ثم enter من لوحة المفاتيح.
تفاصيل عبارة switch:
عندما ننظر إلى عبارة switch نرى عدة مجموعات تبدأ بتعبير التحكم ( السطر 63) وتنتهي بالحالة الإفتراضية ( default ) , وهذا هو الشكل الغالب للعبارة.
المجموعات تسمى حالات , والتعبير عن كل حالة بعبارة شريط ( label ), وهي كلمة case وبعد فراغ ومن ثم التعبير أو المتغير ثم نقطتين فوق بعض.
في كل مجموعة سوف يتم تنفيذ معين ( على سبيل المثال السطر 73 ) , ويمكن أن ندرس أكثر من شرط وننفذ أكثر من أجراء في كل مجموعة.
في مثالنا هذا ( الكود 5.10 السطور 62-99 ) متغير التحكم هو grade حيث هو من يتم مقارنته في كل حالة ( case ).
في الدورة الأولى , ندخل الحرف , ثم يأتي التنفيذ عن عبارة switch حيث يتم مقارنة الحرف المدخل في كل حالة , فعندنا يكتب في الكود : 'case 'a
فإن هذا يعني , ان هل قيمة المتغير grade تساوي قيمة الحرف السابق , فإذا تحقق ذلك سوف يقوم بزيادة المتغير aCount بواحد, مع ملاحظة أنه لا يوجد مقارنة
واحدة في مجموعتنا هذه , أيضا يوجد مقارنة ثانية وهي : 'case 'A , ويمكن قراءة المقارنتين كالتالي ( هل المتغير grade يساوي المتغير a أو A إذا تحقق
ذلك قم بزيادة العداد aCount بواحد ) , وهكذا على باقي المجموعات.
هناك عبارة بالغة الأهمية في العبارة switch وهي فكرتها واساسها , العبارة هي break. وهي التي تخرج التحكم من العبارة في حال تحقق احدى الحالات.
نحن قلنا انه إذا كان العدد المدخل هو الحرف a أو A فإننا نزيد العداد aCount بواحد , ثم ماذا بعد ؟!! بعد ذلك سوف نأتي للعبارة break لتخرجنا
من عبارة الـ switch بأكلمها. إذا لم تكون هذه الكلمة موجودة , سوف تتداخل الأشرطة مع بعضها البعض. ( جرب الإستغناء عنها و ترقب الحدث , سوف يتمثل
لك رياضي يقفز الحواجز , واذا به يتعثر في أحدها ليخفق في المتبقية ).
نأتي إلى نهاية عبارة switch حيث العبارة default, هذه العبارة هي عبارة الإستثناء في السلسلة , حيث اذا لم تححق أي حالة من الحالات في العبارة switch
فسوف يتم تنفيذ مايلي هذه الكلمة من اجراءات.
خطأ برمجي شائع : نسيان عبارة break يولد خطأ منطقي.
خطأ برمجي شائع : تجاهل الفراغ بين كلمة case والقيمة المتكاملة يولد خطأ منطقي. مثلا , كتابة :case3 بدلا من :case 3, حيث تعتبر شريط
غير مستخدمفي الـ switch.
لممارسة البرمجة بشكل جيد : الـ default تساعد على ظم الإستثنائات الشرطية ويفضل وضعها في الأخير.
لممارسة البرمجة بشكل جيد : عبارة الـ default لا تحتاج العبارة break ولكنها توضع هنا لتماثل الكود.
مخطط الحركة الـ UML للعبارة switch:
نلاحظ وجود عبارة الـ break تعقب كل إجراء , وبعد هذه العبارة معين دمج وذلك من أجل الإنتقال إلى خارج الـ switch.
عبارة break وعبارة continue
بالإضافة إلى عبارات الأختيار والتكرار , السي++ تعطي عبارتي الـ break والـ continue لتبديل سير التحكم.
االموضوع السابق عرض كيف الـ break تنهي تنفيذ عبارة الـ switch, هذا الموضوع سوف يشرح كيف تستخدم هاتين
العبارتين في حلقات التكرار.
عبارة break :
عندما تنفذ هذه العبارة في حلقة تكرار فإنها تسبب مباشرة في الخروج منها للإنتقال للتالي. هذه العبارة تريح المعالج من تنفيذ باقي
العمليات ( أو العبارات ) للهروب مبكرا والتجاوز لما هو مهم وفعال ( وذلك كما رأينا في عبارة switch في الكود 5.10 ).
المثال التالي يوضح عمل العبارة break ,
كود 5.13
1 // Fig. 5.13: fig05_13.cpp
2 // break statement exiting a for statement.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int count; // control variable also used after loop terminates
10
11 for ( count = 1; count <= 10; count++ ) // loop 10 times
12 {
13 if ( count == 5 )
14 break; // break loop only if x is 5
15
16 cout << count << " ";
17 } // end for
18
19 cout << "\nBroke out of loop at count = " << count << endl;
20 return 0; // indicate successful termination
21 } // end mainعندما تكتشف عبارة الـ if أن الـ count تساوي 5 , سوف تخرج مباشرة من التكرار for مباشرة ( تنقل إلى السطر 18 ).
وعندما تكون هناك عبارات تكرار متداخلة , فإن عمل عبارة break هو اخراج التنفيذ من عبارة التكرار الحالية , إلى عبارة التكرار
التي تليها ( تحتضن الحالية ).
عبارة continue :
هذه العبارة تلغى اجراءات معينة خلال دورة معينة , اي تقوم بالقفز للدورة التالية دون إكمال مايليها.
سوف نأخذ المثال التالي للتوضيح أكثر ,
كود 5.14
1 // Fig. 5.14: fig05_14.cpp
2 // continue statement terminating an iteration of a for statement.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 for ( int count = 1; count <= 10; count++ ) // loop 10 times
10 {
11 if ( count == 5 ) // if count is 5,
12 continue; // skip remaining code in loop
13
14 cout << count << " ";
15 } // end for
16
17 cout << "\nUsed continue to skip printing 5" << endl;
18 return 0; // indicate successful termination
19 } // end mainكما تشاهدون في مخرجات البرنامج , فإن العدد 5 لم تتم طباعته في سطر الأعداد المطبوع , حيث قامت عبارة continue للقفز للعد التالي.
العمليات المنطقية
كل ما تم شرحة حتى الان هي الشروط البسيطة مثل counter <= 10 , total > 1000 , number != sentinel .
فقط اكتفينا بالعمليات العلائقة ( < , > , == , =! ) , وكما تلاحظون في الأكواد السابقة. ولمزيد من المرونه في التحكم سوف نتناول طرق
جديدة في المقارنة , للحصول على المزيد من الطرق التي توفر لنا المزيد من المرونه والدقة لكتابة اكود دقيقة وبسيطة وسريعة ( في التنفيذ ).
توفر لنا السي++ العمليات المنطقة التالي :
العملية المنطقة AND ( && ) :
عندما يكون لدينا مجموعة بيانات ( تقارير ) ونريد أن نتحقق من صحتها , فكيف لنا أن نتأكد من صحة
شرطين أو أكثر . بالطبع سوف نضطر لإستخدام احدى العمليات المنطقية التي تتأكد من صحة مجموعة
من المقارنات مجتمعة , هذه العبارة ( AND ) تتحق من شرطين , فإذا كان صحيحين فإنها تعطي
نتيجة صحية , وغير ذلك سوف تعطينا نتيجة خاطئة.
الجدول التالي يختصر نتائج المقارنات المحتملة
العبارة المنطقية OR ( || ) :
عندما يكون لدينا مجموعة بيانات ( تقارير ) ونريد أن نتحقق من صحتها , فكيف لنا أن نتأكد من صحة
شرطين أو أكثر . بالطبع سوف نضطر لإستخدام احدى العمليات المنطقية التي تتأكد من صحة مجموعة
من المقارنات مجتمعة , هذه العبارة ( OR ) تتحق من شرطين , فإذا كان خاطئين فإنها تعطي
نتيجة خاطئة , وغير ذلك سوف تعطينا نتيجة صحيحة.
الجدول التالي يختصر نتائج المقارنات المحتملة
العملية المنطقية NOT ( ! ) :
تعكس النتيجة , من خاطئة لصائبة والعكس.
الجدول التالي يختصر نتائج المقارنات المحتملة
مثال على العمليات المنطقية :
كود 5.18
1 // Fig. 5.18: fig05_18.cpp
2 // Logical operators.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6 using std::boolalpha; // causes bool values to print as "true" or "false"
7
8 int main()
9 {
10 // create truth table for && (logical AND) operator
11 cout << boolalpha << "Logical AND (&&)"
12 << "\nfalse && false: " << ( false && false )
13 << "\nfalse && true: " << ( false && true )
14 << "\ntrue && false: " << ( true && false )
15 << "\ntrue && true: " << ( true && true ) << "\n\n";
16
17 // create truth table for || (logical OR) operator
18 cout << "Logical OR (||)"
19 << "\nfalse || false: " << ( false || false )
20 << "\nfalse || true: " << ( false || true )
21 << "\ntrue || false: " << ( true || false )
22 << "\ntrue || true: " << ( true || true ) << "\n\n";
23
24 // create truth table for ! (logical negation) operator
25 cout << "Logical NOT (!)"
26 << "\n!false: " << ( !false )
27 << "\n!true: " << ( !true ) << endl;
28 return 0; // indicate successful termination
29 } // end mainالجديد الملاحظ في هذا الكود , استخداما boolalpha من مساحة الأسماء std كما هو واضح في السطر 5 والسطر 11.
وأداة الإخراج هذه تقوم بإستبدال نتائج المقارنات بالكلمات true أو false بدلا من الـصفر والواحد كنتائج للمقارنات المنطقية.
استخدمت في السطر 11 , وسرى تأثيرها على كل عمليات الإخراج التي تليها.
ملخص اسبقية وترابطية العمليات :
عند تطبيقنا للعميلة , فإن الأولويات والترابطيات بين التعابير الحسابية تكون وفق التالي :
مالذي يحدث عند الخلط بين المقارنة ( == ) وعملية الإسناد ( = ) ؟
عندما نريد أن نستخدم ( == ) ونخطيء ونستخدم ( = ) , في عملية المقارنة , كالتالي :
[if ( payCode = 4 ) cout << " you get bonus " ;
النتيجة , لن يحدث خطأ نصي يعيق تنفيذ البرنامج , بل يحدث خطأ منطقي !!
مايحدث هو أن نتيجة الشرط سوف تكون صحيحة وعبارة الشرط سوف تنفذ
في كل الأحوال.
وهناك شي آخر سوف يحدث , عملية اسناد القيمة 4 إلى المتغير payCode.
عندما نريد أن نستخدم ( = ) ونخطيء ونستخدم ( == ) , في عملية الإسناد , كالتالي :
بدلا من أن نكتب
;x= 1
نكتب
;x == 1
أيضا لا يحدث شي !!
كل ما يحدث , ان تتم عملية المقارنة, ونحصل على النتيجة , وعند تجاوز التنفيذ للسطر , تضيع قيمة المقارنة وكأن شيئا ً لم يحدث.
بما أن هذه الأخطاء لا تنتج رسائل أخطاء عند الترجمة كي ينتبه المبرمج لذلك , مالذي يجب فعله لتجنب هذه الأخطاء ؟ ( هذا سؤال موجه لكم )
انتهت المحاضرة



