7-4
عبارت تكرار
while
يكعبارتتكراربهبرنامه
نويسامكانمي
دهدتابرمبن
اي برقرار بودن يا نبودن مقداري در يك شرط، يك عمل را چندين بار و به تكرار انجام دهد. عبارت شبهكد زير يك فرآيند تكرار شوند را در حين خريد نشان ميدهد:
While there are more items on my shopping list
Purchase next item and cross it off my list
شرط "
there are more items on my shopping list
" ممكن است برقرار يا برقرار نباشد. اگر شرط برقرار باشد عمل "
Purchase next item
" و "
Cross it off my list
" به ترتيب اجرا خواهند شد. اين عمل ميتواند تا زماني كه شرط برقرار است انجام شود. عبارت موجود در ساختار تكرار
while
تشكيل دهنده بدنه
while
است. سرانجام، زمانيكه شرط برقرار نباشد، تكرار پايان يافته و اولين دستور قرار گرفته پس از عبارت تكرار به اجرا در ميآيد
.
مثالي كه در زير آورده شده از عبارت
while
استفاده كرده و اولين توان
3
بزرگتر از
100
را پيدا ميكند. در ابتداي كار متغير
product
با
3
مقدار دهي اوليه شده است
:
int product = 3;
while (product <= 100)
product = product * 3;
هنگامي كه برنامه وارد عبارت
while
ميشود، مقدار متغير
product
برابر
3
است. متغير
product
بصورت مكرر در
3
ضرب ميشود و مقادير
9
،
27
،
81
و
243
بدست ميآيند. زمانيكه مقدار
product
برابر
243
شود، شرط
product<=100
در عبارت
while
برقرار نخواهد شد. در چنين حالتي تكرار با مقدار
243
براي
product
پايان ميپذيرد. اجراي برنامه با عبارت بعد از
while
دنبال ميشود. [نكته: اگر شرط يك عبارت
while
در همان ابتدا برقرار نباشد، عبارات قرار گرفته در بدنه عبارت تكرار اجرا نخواهند شد.]
خطاي برنامهنويسي
شرط عبارت تكرار را به نوعي تنظيم نمائيد كه برنامه براي هميشه در داخل حلقه قرار نگيرد، در غير اينصورت برنامه در حلقه بينهايت
“infinite loop”
گرفتار ميشود.
دياگرام فعاليت
UML
به نمايش درآمده در شكل 6-4 نشاندهنده روند كنترلي است كه متناظر با عبارت
while
مطرح شده در قسمت فوق ميباشد. مجدداً نمادهاي موجود در اين دياگرام نشاندهنده يك وضعيت عمل و يك تصميمگيري هستند. همچنين اين دياگرام مبادرت به معرفي نماد ادغام
UML
كرده است، كه دو روند يا جريان فعاليت را به يك فعاليت متصل ميكند.
UML
نماد ادغام و تصميمگيري را بصورت لوزي نشان ميدهد. در اين دياگرام، نماد ادغام مبادرت به پيوند انتقالها از وضعيت اوليه و از وضعيت عمل كرده است، از اينرو هر دو جريان وارد بخش تصميم شدهاند كه تعيين ميكند آيا حلقه مجدداً بايد تكرار شود يا خير. ميتوان نمادهاي تصميم و ادغام را توسط تعداد فلشهاي انتقال «واردشونده» و «خارجشونده» تشخيص داد. نماد تصميم داراي يك فلش انتقال اشارهكننده به لوزي داشته و داراي دو يا چند فلش انتقال اشارهكننده به خارج از لوزي است كه نشاندهنده انتقالات ممكنه از اين نقطه هستند. علاوه بر اين، هر فلش انتقال اشارهكننده به خارج از نماد تصميم داراي يك نگهبان شرط در كنار خود است. در طرف ديگر، نماد ادغام قرار دارد كه داراي دو يا چند فلش انتقال اشارهكننده به لوزي است و فقط يك فلش انتقال از آن خارج ميشود و نشان ميهد كه چندين روند با يكديگر براي انجام فعاليت ادغام شدهاند. توجه كنيد، كه برخلاف نماد تصميم، نماد ادغام داراي يك رونوشت در كد
C++
نيست.
دياگرام شكل 6-4 بوضوح عمليات تكرار در عبارت
while
مطرح شده در ابتداي اين بخش را نشان ميدهد. فلش انتقال از وضعيت عمل به حالت ادغام اشاره ميكند، كه انتقال را به تصميم باز ميگرداند تا تستي داير بر اينكه آيا حلقه دوباره بايد صورت گيرد يا خير انجام دهد، اين حلقه زماني شكسته ميشود كه شرط
product>100
برقرار گردد. پس از خاتمه عمليات
while
، كنترل به عبارت بعدي در برنامه انتقال مييابد (در اين مورد وضعيت پاياني).
ميتوانيد دياگرامهاي فعاليت
UML
خالي عبارت تكرار
while
را تصور كنيد كه برنامهنويسان ميتوانند هر تعداد از آنها را به روش پشتهاي يا تودرتو با ساير دياگرامهاي فعاليت عبارات كنترلي بكار گيرند تا بخشي از الگوريتم را پيادهسازي كنند.
شكل 6-4 | دياگرام فعاليت
UML
عبارت تكرار
while
.
8-4 فرموله كردن الگوريتم: شمارنده-كنترل تكرار
براي اينكه با نحوه توسعه الگوريتمها آشنا شويد، مسئله بدست آوردن ميانگين نمرات يك كلاس را با روشهاي مختلف بررسي ميكنيم. صورت مسئله عبارت است از:
از كلاسي با ده دانشآموز آزموني بعمل آمده است. نمرات اين آزمون در اختيار شما قرار دارد (نمرات در محدودة
0
تا
100
هستند). مجموع نمرات دانشآموزان و ميانگين نمرات اين كلاس را بدست آوريد.
ميانگين كلاس عبارت است از مجموع نمرات تقسيم بر تعداد دانشآموزان. الگوريتم بكار رفته بر روي كامپيوتر به منظور حل اين مسئله بايستي تك تك نمرات را به عنوان ورودي دريافت كرده، محاسبه ميانگين را انجام داده و نتيجه را به نمايش در آورد.
الگوريتم شبهكد با شمارنده-كنترل تكرار
اجازه دهيد تا از شبهكد استفاده كرده و ليستي از فعاليتهاي اجرائي تهيه و ترتيب اجرا را مشخص سازيم. از روش شمارنده-كنترل تكرار براي دريافت تك تك نمرات بعنوان ورودي استفاده ميكنيم. در اين تكنيك از متغيري بنام شمارنده
(counter)
براي تعيين تعداد دفعات مجموعهاي از عبارات كه اجرا خواهند شد، استفاده ميشود. روش شمارنده-كنترل تكرار، روش تكرار تعريف شده نيز ناميده ميشود چرا كه تعداد تكرار قبل از اينكه حلقه آغاز شود، مشخص است. در اين مثال، اجراي حلقه با رسيدن شمارنده به
10
خاتمه مييابد. در اين بخش به معرفي الگوريتم شبهكد (شكل 7-4) و نسخهاي از كلاس
GradeBook
(شكلهاي 8-4 و 9-4)
ميپردازيم كه الگوريتم را توسط يك تابع عضو
C++
پيادهسازي ميكند
. در بخش 9-4 با توسعه الگوريتمها با استفاده از شبهكد آشنا خواهيد شد.
به نقش
total
و
counter
در الگوريتم شبهكد دقت كنيد (شكل 7-4). در واقع
total
متغيري است كه از آن براي محاسبه مجموع مقادير استفاده ميشود و
counter
متغيري است كه نقش شمارنده بر عهده دارد، در اين برنامه، شمارنده تعداد نمرات وارد شدة توسط كاربر را ثبت ميكند.
Set grade counter to one
While grade counter is less than or equal to 10
Input the next grade
Add the grade to the total
Add one to the grade counter
Set the class average to the total divided by 10
شكل7-4 | الگوريتم شبهكد با استفاده از روش شمارنده-كنترل تكرار براي حل مسئله ميانگين كلاس.
1
// Fig. 4.8: GradeBook.h
2
// Definition of class GradeBook that determines a class average.
3
// Member functions are defined in GradeBook.cpp
4
include <string> // program uses C++ standard string class
5
using std::string;
6
7
// GradeBook class definition
8
class GradeBook
9
{
10
public:
11
GradeBook( string ); // constructor initializes course name
12
void setCourseName( string ); // function to set the course name
13
string getCourseName(); // function to retrieve the course name
14
void displayMessage(); // display a welcome message
15
void determineClassAverage(); // averages grades entered by the user
16
private:
17
string courseName; // course name for this GradeBook
18
}; // end class GradeBook
شكل
8-4 |
مسئلهميانگينكلاسبااستف
اده از روش شمارنده-كنترل تكرار:سرآيند فايل
GradeBook
.
1
// Fig. 4.9: GradeBook.cpp
2
// Member-function definitions for class GradeBook that solves the
3
// class average program with counter-controlled repetition.
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
GradeBook::GradeBook( string name )
13
{
14
setCourseName( name ); // validate and store courseName
15
} // end GradeBook constructor
16
17
// function to set the course name;
18
// ensures that the course name has at most 25 characters
19
void GradeBook::setCourseName( string name )
20
{
21
if ( name.length() <= 25 ) // if name has 25 or fewer characters
22
courseName = name; // store the course name in the object
23
else // if name is longer than 25 characters
24
{ // set courseName to first 25 characters of parameter name
25
courseName = name.substr( 0, 25 ); // select first 25 characters
26
cout << "Name \"" << name << "\" exceeds maximum length (25).\n"
27
<< "Limiting courseName to first 25 characters.\n" << endl;
28
} // end if...else
29
} // end function setCourseName
30
31
// function to retrieve the course name
32
string GradeBook::getCourseName()
33
{
34
return courseName;
35
} // end function getCourseName
36
37
// display a welcome message to the GradeBook user
38
void GradeBook::displayMessage()
39
{
40
cout << "Welcome to the grade book for\n" << getCourseName() << "!\n"
41
<< endl;
42
} // end function displayMessage
43
44
// determine class average based on 10 grades entered by user
45
void GradeBook::determineClassAverage()
46
{
47
int total; // sum of grades entered by user
48
int gradeCounter; // number of the grade to be entered next
49
int grade; // grade value entered by user
50
int average; // average of grades
51
52
// initialization phase
53
total = 0; // initialize total
54
gradeCounter = 1; // initialize loop counter
55
56
// processing phase
57
while ( gradeCounter <= 10 ) // loop 10 times
58
{
59
cout << "Enter grade: "; // prompt for input
60
cin >> grade; // input next grade
61
total = total + grade; // add grade to total
62
gradeCounter = gradeCounter + 1; // increment counter by 1
63
} // end while
64
65
// termination phase
66
average = total / 10; // integer division yields integer result
67
68
// display total and average of grades
69
cout << "\nTotal of all 10 grades is " << total << endl;
70
cout << "Class average is " << average << endl;
71
} // end function determineClassAverage
شكل
9-4 |
مسئلهميانگينكلاسبااستفادهازروششمارنده
-
كنترلتكرار
:
كدمنبعفايل
GradeBook
.
افزايش قابليت اعتبارسنجي
GradeBook
قبل از اينكه به بحث پيادهسازي الگوريتم ميانگين كلاس بپردازيم، اجازه دهيد به بهبود كارائي انجام گرفته بر روي كلاس
GradeBook
توجه كنيم. در برنامه 16-3، تابع
setCourseName
مبادرت به اعتبارسنجي نام دوره با تست طول نام دور ميكرد، كه بايد كمتر يا برابر
25
كاراكتر باشد (با استفاده از يك عبارت
if
). اگر شرط برقرار بود، نام دوره بكار گرفته ميشد. سپس اين كد با يك عبارت
if
ديگر دنبال
مي
شد كه مبادرت به تست طول نام دوره ميكرد كه آيا بزرگتر از
25
كاراكتر است يا خير. دقت كنيد كه شرط عبارت
if
دوم كاملاً متضاد شرط
if
اول است. اگر شرطي با
true
ارزيابي گردد، بايستي شرطهاي ديگر با
false
ارزيابي شوند. پيادهسازي چنين وضعيتي توسط عبارت
if..else
بهتر خواهد بود، از اينرو كد خود را با جايگزين كردن دو عبارت
if
با يك عبارت
if..else
اصلاح كردهايم (خطوط
21-28
از برنامه شكل 9-4).
1
// Fig. 4.10: fig04_10.cpp
2
// Create GradeBook object and invoke its determineClassAverage function.
3
include "GradeBook.h" // include definition of class GradeBook
4
5
int main()
6
{
7
// create GradeBook object myGradeBook and
8
// pass course name to constructor
9
GradeBook myGradeBook( "CS101 C++ Programming" );
10
11
myGradeBook.displayMessage(); // display welcome message
12
myGradeBook.determineClassAverage
(); // find average of 10 grades
13
return 0; // indicate successful termination
14
} // end main
شكل10-4| برنامه ميانگين كلاس با شمارنده-كنترل تكرار:ايجاد يك شي از كلاس
GradeBook
(شكل 8-4 و 9-4) و فراخواني تابع عضو
determineClassAverage
.
پيادهسازي شمارنده-كنترل تكرار در كلاس
GradeBook
كلاس
GradeBook
(شكل 8-4 و 9-4) حاوي يك سازنده (اعلان شده در خط
11
از شكل 8-4 و تعريف شده در خطوط
12-15
از شكل 9-4) است كه مبادرت به تخصيص مقداري به متغير نمونه كلاس
courseName
ميكند (اعلان شده در خط
17
از شكل 8-4). در خطوط
19-29
،
32-35
و
38-42
از شكل 9-4 توابع عضو
setCourseName
،
getCourseName
و
displayMessage
تعريف شدهاند. در خطوط
45-71
تابع عضو
determineClassAverage
تعريف شده است كه پياده سازيكننده الگوريتم ميانگين كلاس توضيح داده شده در شبه كد شكل 7-4 است.
در خطوط
47-50
متغيرهاي محلي
total
،
gradeCounter
،
grade
و
average
از نوع
int
اعلان شدهاند. در متغير
grade
ورودي كاربر ذخيره ميشود. توجه كنيد كه اعلانهاي فوق در بدنه تابع عضو
determineClassAverage
قرار دارند.
در نسخههاي كلاس
GradeBook
مطرح شده در اين فصل، فرآيند خواندن و پردازش نمرات به روش سادهاي در نظر گرفته شدهاند. محاسبه ميانگين در تابع عضو
determineClassAverage
و با استفاده از متغيرهاي محلي صورت ميگيرد. در اين بخش مبادرت به ذخيرهسازي نمرات دانشجويان نميكنيم. در فصل هفتم، با تغييري كه در كلاس
GradeBook
انجام ميدهيم قادر به نگهداري نمرات در حافظه خواهيم بود كه توسط ساختمان داده آرايه صورت ميگيرد. در اينحالت به يك شي
GradeBook
اجازه داده ميشود تا محاسبات مختلف را بر روي همان مجموعه از نمرات انجام دهد بدون اينكه كاربر مجبور به وارد كردن همان نمرات به دفعات باشد.
برنامهنويسي ايدهال
هميشه يك خط خالي مابين بخش اعلانها و عبارات اجرايي قرار دهيد. در اين صورت بخش اعلان بخوبي در برنامه مشخص شده و خوانايي برنامه افزايش مييابد.
در خطوط
53-54
متغير
total
با
0
و
gradeCounter
با
1
مقداردهي اوليه شدهاند.دقت كنيد كه متغيرهاي
totoal
و
gradeCounter
قبل از اينكه در محاسبات بكار گرفته شوند، مقداردهي اوليه شدهاند.
معمولاً متغيرهاي شمارنده را با يك يا صفر و بر اساس نياز مقداردهي اوليه ميكنند. يك متغير مقداردهي نشده حاوي يك مقدار اشغال (يا مقدار تعريف نشده) است، آخرين مقداري كه در مكان حافظه رزرو شده براي متغير از قبل وجود داشته است. در اين برنامه متغيرهاي
grade
و
average
كه مقدار خود را از طرف ورودي كاربر و محاسبه ميانگين بدست ميآورند،
نيازي به مقداردهي اوليه ندارند.
خط
57
مشخص ميكند كه عبارت
while
تا زمانيكه مقدار
gradeCounter
كمتر يا معادل
10
باشد، تكرار خواهد شد. تا زمانيكه شرط برقرار باشد، ساختار
while
عبارات قرار گرفته مابين براكتهاي بدنه خود را تكرار خواهد كرد.
عبارت بكار رفته در خط
59
، جمله
"Enter grade:"
را بنمايش در ميآورد. اين خط معادل عبارت شبهكد
“Prompt the user to enter the next grade.”
هستند. خط
60
مقدار وارد شده توسط كاربر را خوانده و آنرا در متغير
grade
ذخيره ميكند. اين خط معادل شبهكد
"Input the next grade."
است. بخاطر داريد كه متغير
grade
در ابتداي برنامه مقداردهي اوليه نشده است، به اين دليل كه برنامه مقدار
grade
را از كاربر و در هر بار تكرار حلقه اخذ ميكند.
سپس، برنامه مقدار
total
را با مقدار جديد
grade
كه توسط كاربر وارد شده به روز ميكند (خط
61
)
. مقدار
grade
با مقدار قبلي
total
جمع شده و نتيجه به
total
تخصيص مييابد.
در خط
62
متغير
gradeCounter
يك واحد افزايش مييابد تا نشان دهد يك نمره مورد پردازش قرار گرفته است. اينكار تا زمانيكه شرط موجود در عبارت
while
برقرار نشود، ادامه مييابد. پس از اتمام حلقه، در خط
66
نتيجه محاسبه ميانگين به متغير
average
تخصيص مييابد. خط
69
پيغام
"Total of all 10 grades is"
و بدنبال آن مقدار متغير
total
را بنمايش در ميآورد. سپس
در خط
70
پيغامي حاوي رشته
“Class average is”
كه بدنبال آن مقدار متغير
average
آورده شده، به نمايش در ميآيد. تابع عضو
determineClassAverage
، كنترل را به تابع فراخوان برگشت ميدهد (تابع
main
در شكل 10-4).
توصيف كلاس
GradeBook
شكل 10-4 حاوي تابع
main
اين برنامه است، كه يك شي از كلاس
GradeBook
ايجاد و به توصيف قابليتهاي آن ميپردازد. در خط
9
از شكل10-4 يك شي جديد از
GradeBook
بنام
myGradeBook
ايجاد ميشود. رشته موجود در خط
9
به سازنده
GradeBook
ارسال ميشود (خطوط 15-12 از شكل 9-4). خط
11
از شكل10-4 تابع عضو
displayMessage
را براي نمايش پيغام خوشآمدگويي به كاربر فراخواني ميكند. سپس خط
12
تابع عضو
determineClassAverage
را فراخواني ميكند تا كاربر بتواند
10
نمره را وارد كرده و سپس ميانگين را محاسبه و چاپ ميكند. تابع عضو، الگوريتم نشان داده شده در شبه كد شكل 7-4 را انجام ميدهد.
نكاتي در ارتباط با تقسيم صحيح و قطع كردن
محاسبه ميانگين توسط تابع عضو
determineClassAverage
صورت مي
گيرد كه در واكنش به فراخواني تابع در خط
12
از شكل10-4 فعال شده و يك عدد صحيح توليد ميكند. خروجي برنامه نشان ميدهد كه مجموع نمرات در اجراي نمونه برنامه
846
است كه به هنگام تقسيم بر
10
، بايد
84.6
بدست آيد، عددي با نقطه اعشار. با اين همه، در نتيجه محاسبه
total/10
عدد
84
بدست آمده است (خط
66
از شكل 9-4)، چرا كه
total
و
10
هر دو مقادير عددي صحيح هستند. نتيجه تقسيم دو عدد صحيح يك عدد صحيح است كه در آن بخش اعشاري بدست آمده از تقسيم حذف ميگردد (قطع ميشود). در بخش بعد با نحوه بدست آوردن نتايج اعشاري از محاسبات آشنا خواهيد شد.
خطاي برنامهنويسي
فرض اينكه تقسيم صحيح مبادرت به گرد كردن (بجاي قطع كردن) ميكند ميتواند نتايج اشتباهي بدنبال داشته باشد. براي مثال،
7
¸
4
حاصل
1.75
را در رياضي بدست ميدهد، در حاليكه در يك تقسيم صحيح
1
كوتاه شده و
2
در حالت گرد شده توليد ميكند.
در برنامه شكل 9-4، اگر در خط
66
از
gradeCounter
بجاي
10
در محاسبه استفاده شود، خروجي اين برنامه مقدار اشتباه
76
را نشان خواهد داد. دليل اينكار در آخرين تكرار عبارت
while
نهفته است كه
gradeCounter
به مقدار
11
در خط
62
افزايش يافته است.
خطاي برنامهنويسي
استفاده از متغير شمارنده حلقه، در يك عبارت محاسباتي پس از حلقه، معمولاً سبب توليد خطاي منطقي بنام
off-by-one-error
ميشود.
9-4 فرموله كردن الگوريتمها: مراقبت-كنترل تكرار
اجازه دهيد تا به مسئله ميانگين كلاس بازگرديم و آنرا مجدداً و اينبار بصورت زير و كليتر تعريف كنيم:
"برنامه محاسبه ميانگين كلاس را به نحوي توسعه دهيد تا در هر بار اجراي برنامه، به تعداد اختياري نمره دريافت كرده و محاسبه ميانگين بر روي آنها اعمال شود."
در برنامه قبلي، تعداد نمرات از همان ابتدا مشخص بود (
10
نمره). در اين برنامه، تعداد نمراتي كه بعنوان ورودي وارد خواهند شد مشخص نيستند. برنامه بايد بر روي تعداد نمرات وارد شده كار كند. چگونه برنامه تشخيص ميدهد كه به گرفتن نمره پايان دهد؟ محاسبات به چه صورتي بايد انجام گرفته و ميانگين كلاس به نمايش درآيد؟
يك راهحل براي رفع اين مشكل، استفاده از يك مقدار ويژه بنام مقدار مراقبتي
(sentinel value)
است كه پايان ورود دادهها را مشخص ميكند (همچنين به اين مقدار، مقدار سيگنال، مقدار ساختگي يا پرچم نيز ميگويند). در اين روش كاربر اقدام به وارد كردن نمرهها كرده و در پايان مقدار مراقبتي تعيين شده را به عنوان اينكه دادههاي ورودي به اتمام رسيدهاند، وارد ميسازد. روش مراقبت-كنترل تكرار، روش تكرار-تعريفنشده نيز ناميده ميشود چرا كه تعداد دفعات تكرار قبل از اجراي حلقه مشخص نيست.
واضح است كه مقدار مراقبتي بايد به نحوي انتخاب شود كه به عنوان يك ورودي معتبر مورد قبول واقع نشود. بدليل اينكه نمرات امتحان معمولاً منفي نيستند، ميتوانيم از مقدار
-1
به عنوان مقدار مراقبتي در اين برنامه استفاده كنيم. بنابراين به هنگام اجراي برنامه، نمرات كلاس، ميتوانند ترتيبي مانند
-1
و
84
،
74
،
75
،
96
،
93
داشته باشند. برنامه بايد نمره ميانگين كلاس را با استفاده از مقادير
93
،
96
،
75
،
74
و
84
محاسبه كرده و به نمايش درآورد (
-1
يك مقدار مراقبتي است و نبايد در محاسبه ميانگين وارد شود).
خطاي برنامهنويسي
انتخاب يك مقدار مراقبتي به عنوان يك داده معتبر، موجب رخ دادن خطاي منطقي ميشود.
الگوريتم شبهكد به روش مراقبت كنترل تكرار به روش از بالا به پايين، اصلاح گامبهگام: اولين اصلاح
به هنگام بررسي مسائل پيچيدهاي همانند اين برنامه، عرضه الگوريتم شبهكد به آساني امكان پذير نميباشد. از اينرو به برنامه ميانگين كلاس با استفاده از تكنيكي بنام، از بالا به پايين، اصلاح گامبهگام نزديك ميشويم كه براي ساخت و توسعه برنامههاي ساختيافته مناسب و ضروري است. شبهكدي كه در بالاترين سطح
(top)
ارائه ميشود، عبارت است از:
Determine the class average for the quiz.
اين عبارت تابع و هدف اصلي برنامه است كه در واقع كاري كه بايد برنامه انجام دهد را در بردارد. عبارت
top
جزئيات ناكافي در مورد اينكه برنامه چگونه بايستي نوشته شود در خود دارد. بنابر اين به طرف جزئيات برنامه و اصلاح گام به گام پيش ميرويم. ابتدا عبارت
top
به قسمتهاي كوچكي تقسيم ميشود كه هر يك به ترتيب وظايفي در برنامه ايفا ميكنند. نتيجه اين تقسيمات در اولين گام ميتواند چنين باشد:
Initialize Variables
Input, sum and count the quiz grades
Calculate and print the total of all student grades and the class average
در اينجا، با توجه به اينكه فقط از عبارت توالي استفاده شده، ليست انجام مراحل فقط شامل عبارتهاي اجرائي است كه به ترتيب يكي پس از ديگري اجرا ميشوند.
اصلاح گامبهگام مرحله دوم
مرحله بعدي تجزيه برنامه به جزئيات بيشتر (مرحله دوم)، در ارتباط با متغيرها ميباشد به يك متغير بنام
total
نياز است كه مجموع اعداد را در خود نگهداري كند و به يك متغير ديگر بنام
count
كه نشان دهد، چه تعدادي از اين اعداد مورد پردازش قرار گرفتهاند. يك متغير براي دريافت هر نمره از طريق ورودي و يك متغير براي نگهداري ميانگين محاسبه شده مورد نياز است. عبارت شبهكد:
Initialize variable
را ميتوان به عبارات جزئيتر زير تقسيم كرد:
Initialize total to zero
Initialize counter to zero
توجه كنيد كه فقط متغيرهاي
total
و
counter
نياز به مقداردهي اوليه قبل از بكارگيري دارند. متغيرهاي
average
و
grade
(اين متغيرها براي محاسبه ميانگين و ورودي كاربر استفاده شده است)، نيازي به مقداردهي اوليه ندارند. عبارت شبهكد:
Input, sum and count the quiz grades
نيازمند يك عبارت تكرار (حلقه) است كه نمرات را دريافت كند. چون بطور دقيق نميدانيم كه چه تعداد نمره به عنوان ورودي دريافت خواهيم كرد، از روش مراقبت-كنترل تكرار استفاده ميكنيم. كاربر در هر زمان يك مقدار معتبر وارد ميكند و پس از اينكه آخرين مقدار مورد نظر را وارد كرد، مقدار مراقبتي را وارد ميكند تا از حلقه ورود نمرات خارج شود. برنامه در هر بار كه داده وارد ميشود مقدار آنرا با مقدار مراقبتي مقايسه ميكند. دومين اصلاح بر روي عبارت شبهكد قبلي ميتواند بصورت زير باشد:
Prompt the user to enter the first grade
Input the first grade (possibly the sentinel)
While the user has not yet entered the sentinel
Add this grade to the running total
Add one to the grade counter
Input the next grade (possibly the sentinel)
عبارت شبهكد زير
Calculate and print the total of all student grades and the class average
ميتواند به صورت عبارات جزئيتر زير نوشته شود:
If the counter is not equal to zero
Set the average to the total divided by the counter
Print the total of all student grades in the class
Print the average
Else
Print “No grades were entered”
توجه كنيد كه در اين قسمت براي جلوگيري از بروز خطاي منطقي تقسيم بر صفر، يك تست بكار برده شده است كه اگر در برنامه تشخيص داده نشود، ميتواند مشكل ساز شود. شبهكد كامل برنامه ميانگين در شكل 11-4 آورده شده است.
خطاي برنامهنويسي
نتيجه تقسيم بر صفر خطاي عظيم در زمان اجرا است.
اجتناب از خطا
به هنگام اجراي يك عمل تقسيم بر عبارتي كه ممكن است مقدار آن صفر باشد، بايد تستي به همين منظور و رسيدگي به آن در برنامه تدارك ديده شود. رسيدگي به اين امر ميتواند چاپ يك پيغام ساده خطا باشد. گاهي اوقات انجام عمليات پيچيده مورد نياز است.
Initialize total to zero
Initialize counter to zero
Input the first grade (possibly the sentinel)
While the user has not as yet entered the sentinel
Add this grade to the running total
Add one to the grade counter
Input the next grade (possibly the sentinel)
If the counter is not equal to zero
Set the average to the total divided by the counter
Print the average
Else
Print “No grades were entered”
شكل 11-4 | الگوريتم شبهكد با استفاده از روش مراقبت-كنترل تكرار براي حل مسئله ميانگين كلاس.
برنامهنويسي ايدهال
با قراردادن خطوط خالي در برنامههاي شبهكد خوانائي آنها افزايش مييابد. خطوط خالي موجب ميشوند تا عبارتهاي كنترلي شبهكد و فازهاي برنامه از هم متمايز شوند.
مهندسي نرمافزار
بسياري از الگوريتمها را ميتوان به صورت منطقي به سه فاز تقسيم كرد: فاز مقدار دهي كه در آن متغيرهاي برنامه مقداردهي اوليه ميشوند، فاز پردازش كه مقادير دادهها وارد شده و متغيرها براساس آنها تنظيم ميشوند و فاز پايان كه مرحله انجام محاسبات و چاپ نتايج است.
الگوريتم شبهكد 11-4 مسئله ميانگين كلاس را كه در ابتداي اين بخش بصورت كلي بيان شده بود، برطرف ميكند. اين الگوريتم فقط پس از طي دو مرحله اصلاح گام به گام توسعه يافت، در حاليكه گاهي اوقات به انجام مراحل بيشتر نياز است.
مهندسي نرمافزار
برنامهنويسان زماني به فرآيند از بالا به پايين و اصلاح گام به گام پايان ميدهند كه الگوريتم شبهكد بصورت مشخص جزئيات را بيان كرده باشد، به نحوي كه بتوان آنها را به برنامه
C++
تبديل كرد. در اينحالت پياده سازي برنامه
C++
براحتي ميتواند صورت گيرد.
پيادهسازي كلاس
GradeBook
به روش مراقبت-كنترل تكرار
شكلهاي 12-4 و 13-4 كلاس
GradeBook
را به نحوي نشان ميدهند كه حاوي تابع عضو
determineClassAverage
است كه الگوريتم شبه كد شكل 11-4 را پيادهسازي ميكند (اين كلاس در شكل 14-4 توصيف شده است). اگر چه هر نمره وارد شده يك عدد صحيح است، امكان توليد يك عدد اعشاري به هنگام محاسبه ميانگين وجود دارد، به عبارتي يك عدد حقيقي يا عدد با نقطه اعشار (همانند
7.33
،
0.0975
يا
1000.12345
). نوع داده
int
نمي
تواند چنين اعدادي را عرضه كند، از اينرو اين كلاس بايد از نوع داده ديگري استفاده كند. زبان
C++
داراي چندين نوع داده براي ذخيرهسازي اعداد اعشاري در حافظه است، نوعهاي همانند
float
و
double
. تفاوت اصلي مابين اين نوع در اين است كه در مقايسه با متغيرهاي
float
، متغيرهاي
double
قادر به نگهداري اعداد بزرگتر و دقيقتر در سمت نقطه اعشار هستند، در نتيجه دقت عدد بيشتر خواهد بود. اين برنامه مبادرت به معرفي يك عملگر ويژه بنام عملگر
cast
است كه محاسبه ميانگين را مجبور ميكند تا نتيجه را بصورت عدد اعشاري توليد كند. اين ويژگي به هنگام بررسي برنامه توضيح داده خواهد شد.
در اين مثال مشاهده ميكنيد كه عبارات كنترلي ميتوانند به صورت پشته يكي بر روي ديگري قرار داده شوند (بصورت متوالي). عبارت
while
در خطوط
67-75
از شكل13-4 بلافاصله پس از عبارات
if..else
قرار گرفته است و حالت توالي دارد. قسمت اعظم كد بكار رفته در اين مثال با كد برنامه 9-4 يكسان است، از اينرو تمركز خود را بر روي ويژگيها و مباحث جديد متمركز ميكنيم.
1
// Fig. 4.12: GradeBook.h
2
// Definition of class GradeBook that determines a class average.
3
// Member functions are defined in GradeBook.cpp
4
#include <string> // program uses C++ standard string class
5
using std::string;
6
7
// GradeBook class definition
8
class GradeBook
9
{
10
public:
11
GradeBook( string ); // constructor initializes course name
12
void setCourseName( string ); // function to set the course name
13
string getCourseName(); // function to retrieve the course name
14
void displayMessage(); // display a welcome message
15
void determineClassAverage(); // averages grades entered by the user
16
private:
17
string courseName; // course name for this GradeBook
18
}; // end class GradeBook
شكل
12-4 |
برنامهميانگينكلاسبار
وش مراقبت-كنترل تكرار:
فايل سرآيند
GradeBook
1
// Fig. 4.13: GradeBook.cpp
2
// Member-function definitions for class GradeBook that solves the
3
// class average program with sentinel-controlled repetition.
4
#include <iostream>
5
using std::cout;
6
using std::cin;
7
using std::endl;
8
using std::fixed; // ensures that decimal point is displayed
9
10
#include <iomanip> // parameterized stream manipulators
11
using std::setprecision; // sets numeric output precision
12
13
// include definition of class GradeBook from GradeBook.h
14
#include "GradeBook.h"
15
16
// constructor initializes courseName with string supplied as argument
17
GradeBook::GradeBook( string name )
18
{
19
setCourseName( name ); // validate and store courseName
20
} // end GradeBook constructor
21
22
// function to set the course name;
23
// ensures that the course name has at most 25 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
cout << "Welcome to the grade book for\n" << getCourseName() << "!\n"
46
<< endl;
47
} // end function displayMessage
48
49
// determine class average based on 10 grades entered by user
50
void GradeBook::determineClassAverage()
51
{
52
int total; // sum of grades entered by user
53
int gradeCounter; // number of grades entered
54
int grade; // grade value
55
double average; // number with decimal point for average
56
57
// initialization phase
58
total = 0; // initialize total
59
gradeCounter = 0; // initialize loop counter
60
61
// processing phase
62
// prompt for input and read grade from user
63
cout << "Enter grade or -1 to quit: ";
64
cin >> grade; // input grade or sentinel value
65
66
// loop until sentinel value read from user
67
while ( grade != -1 ) // while grade is not -1
68
{
69
total = total + grade; // add grade to total
70
gradeCounter = gradeCounter + 1; // increment counter
71
72
// prompt for input and read next grade from user
73
cout << "Enter grade or -1 to quit: ";
74
cin >> grade; // input grade or sentinel value
75
} // end while
76
77
// termination phase
78
if ( gradeCounter != 0 ) // if user entered at least one grade...
79
{
80
// calculate average of all grades entered
81
average = static_cast< double >( total ) / gradeCounter;
82
83
// display total and average (with two digits of precision)
84
cout << "\nTotal of all " << gradeCounter << " grades entered is "
85
<< total << endl;
86
cout<< "Class average is" << setprecision( 2 ) << fixed << average
87
<< endl;
88
} // end if
89
else // no grades were entered, so output appropriate message
90
cout << "No grades were entered" << endl;
91
} // end function determineClassAverage
شكل
13-4
| برنامهميانگينكلاسباروشمراقبت
-
كنترلتكرار
:
فايل كد منبع
GradeBook
1
// Fig. 4.14: fig04_14.cpp
2
// Create GradeBook object and invoke its determineClassAverage function.
3
4
// include definition of class GradeBook from GradeBook.h
5
#include "GradeBook.h"
6
7
int main()
8
{
9
// create GradeBook object myGradeBook and
10
// pass course name to constructor
11
GradeBook myGradeBook( "CS101 C++ Programming" );
12
13
myGradeBook.displayMessage(); // display welcome message
14
myGradeBook.determineClassAverage(); // find average of 10 grades
15
return 0; // indicate successful termination
16
} // end main
شكل14-4|برنامه ميانگين كلاس با روش مراقبت-كنترل تكرار: ايجاد يك شي از كلاس
GradeBook
(شكل12-4 و 13-4) و فراخواني تابع عضو
determineClassAverage
.
در خط
55
متغير
average
از نوع
double
اعلان شده است. اين نوع به محاسبه ميانگين امكان ميدهد تا بصورت يك عدد اعشاري در متغير ذخيره گردد. در خط
59
متغير
gradeCounter
با صفر مقداردهي شده چرا كه هنوز نمرهاي وارد نشده است، به ياد داشته باشيد كه اين برنامه از روش مراقبت-كنترل تكرار استفاده ميكند. به منظور ثبت دقيق تعداد نمرات وارد شدة، متغير
gradeCounter
فقط به هنگام وارد شدن يك نمره معتبر بعنوان ورودي، افزايش مييابد.
تفاوتهاي موجود مابين روشهاي مراقبت-كنترل تكرار و شمارنده-كنترل تكرار
به تفاوتهاي موجود ميان روش مراقبت-كنترل تكرار در اين برنامه و شمارنده-كنترل تكرار در برنامه 9-4 توجه كنيد. در روش شمارنده-كنترل تكرار، در هر بار تكرار عبارت
while
(خطوط
57-63
از شكل 9-4)
يك مقدار از سوي كاربر دريافت ميگرديد. در روش مراقبت-كنترل تكرار، قبل از اينكه برنامه به عبارت
while
برسد، يك مقدار (خطوط
63-64
از شكل 13-4)
) دريافت ميشود. اين مقدار تعيين ميكند كه آيا جريان كنترل برنامه وارد بدنه عبارت
while
شود يا خير. اگر شرط عبارت
while
برقرار نباشد (كاربر مقدار مراقبتي وارد كرده باشد)، بدنه عبارت
while
اجرا نخواهد شد (هيچ نمرهاي وارد نميشود). از سوي ديگر، اگر شرط برقرار شود، بدنه اجرا شده و مقدار وارد شدة كاربر بكار گرفته ميشود (به
total
افزوده ميشود، خط
69
). پس از پردازش مقدار، مقدار بعدي قبل از اينكه برنامه به انتهاي بدنه عبارت
while
برسد توسط كاربر وارد ميشود (خطوط
73-74
)
. زمانيكه برنامه به
}
در خط
75
ميرسد، اجرا با تست بعدي در شرط عبارت
while
ادامه مييابد (خط
67
)
. مقدار جديد وارد شده تعيين ميكند كه آيا عبارت بدنه
while
مجدداً اجرا شود يا خير. دقت كنيد كه مقدار بعدي هميشه قبل از اينكه شرط عبارت
while
ارزيابي شود، بلافاصله توسط كاربر وارد ميشود. در اينحالت برنامه ميتواند قبل از اينكه اقدام به پردازش مقداري نمايد، تعيين كند كه آيا آن مقدار، مقدار مراقبتي است يا خير. اگر مقدار مراقبتي باشد، عبارت
while
خاتمه مييابد و مقدار به
total
افزوده نميشود.
پس از خاتمه حلقه، عبارت
if..else
در خطوط
78-90
اجرا ميشود. شرط موجود در خط
78
تعيين ميكند كه آيا نمرهاي وارده شده است يا خير. اگر نمرهاي وارد نشده باشد، بخش
else
(خطوط
89-90
) از عبارت
if..else
اجرا شده و پيغام
"No grades were entered"
را به نمايش درآورده و تابع عضو كنترل را به تابع فراخوان برگشت ميدهد.
به بلوك موجود در حلقه
while
شكل 13-4 دقت كنيد. بدون حضور براكتها، سه عبارت آخر در بدنه حلقه در خارج از حلقه جاي ميگرفتند و اين سبب ميشد كه كامپيوتر اين كد را بصورت زير و نادرست تفسير كند:
// loop until sentinel value read from user
while ( grade != -1 )
total = total + grade; // add grade to total
gradeCounter = gradeCounter + 1; // increment counter
// prompt for input and read next grade from user
cout<< "Enter grade or -1 to quit: ";
cin>> grade;
در اين حالت برنامه دچار يك حلقه بينهايت ميشود در صورتيكه كاربر
1
- را به عنوان اولين نمره وارد نكند (خط
64
).
خطاي برنامهنويسي
فراموش كردن براكتهاي تعيينكننده مرز يك بلوك ميتواند، سببساز خطاهاي منطقي همانند حلقههاي بينهايت شود.
برنامهنويسي ايدهال
در حلقه كنترل مقدار مراقبتي، كه مقداري از كاربر تقاضا ميكند، بايد مقدار مراقبتي به كاربر نشان داده شود.
دقت اعداد اعشاري و نياز حافظه
متغيرهاي از نوع
float
عرضهكننده اعداد با دقت منفرد در نقطه اعشار هستند و داراي هفت رقم معنيدار در سيستمهاي
32
بيتي ميباشند. متغيرهاي از نوع
double
عرضهكننده دقت مضاعف در نقطه اعشار هستند. اين دقت مستلزم دو برابر حافظه مورد نياز براي يك متغير
float
است و داراي
15
رقم معنيدار در سيستمهاي
32
بيتي است (تقريباً دو برابر دقيقتر از متغيرهاي
float
). براي اكثر محاسبات صورت گرفته در برنامهها نوع
float
ميتواند كافي باشد، اما ميتوانيد با استفاده از
double
دقت را تضمين كنيد. در برخي از برنامهها، حتي متغيرهاي از نوع
double
هم كافي نيستند، برنامههايي كه خارج از قلمرو بحث اين كتاب هستند. اكثر برنامهنويسان براي عرضه اعداد اعشاري از نوع
double
استفاده ميكنند. در واقع
C++
بطور پيشفرض با تمام اعداد اعشاري كه در كد منبع برنامه تايپ ميكنيد (همانند
7.33
و
0.0975
) همانند مقادير
double
رفتار ميكند. چنين مقاديري در كد منبع بعنوان ثابتهاي اعشاري شناخته ميشوند.
غالبا اعداد اعشاري در انجام عمليات تقسيم گسترش زيادي پيدا ميكنند. براي مثال با تقسيم
10
بر
3
، نتيجه
3.333333...
با دنبالهاي از
3
هاي نامتناهي خواهد بود. كامپيوتر فضاي ثابتي براي نگهداري چنين مقاديري در اختيار دارد، از اينرو ذخيره سازي مقادير اعشاري فقط بصورت تخميني صورت ميگيرد.
عليرغم اينكه اعداد اعشاري هميشه
100
درصد دقيق نيستند، اما كاربردهاي بسياري دارند. براي مثال، هنگامي كه در مورد حرارت عادي بدن يعني
98.6
صحبت ميكنيم، نيازي نيست تا دقت اعشاري آنرا بسيار دقيق بيان كنيم. زمانيكه به درجه حرارت در يك دماسنج نگاه ميكنيم و آنرا
98.6
ميخوانيم، ممكن است مقدار دقيق آن
98.5999473210643
باشد. اما استفاده از مقدار
98.6
به صورت تخميني در بسياري از موارد ميتواند مناسب و كاربردي باشد.
خطاي برنامهنويسي
استفاده از اعداد اعشاري با فرض اينكه اين اعداد نشاندهنده مقدار كاملاً دقيق هستند (بويژه در عبارات مقايسهاي) ميتواند نتايج اشتباهي بدنبال داشته باشد. اعداد اعشاري تقريبا در تمام كامپيوترها نشاندهنده يك مقدار تقريبي هستند.
تبديل مابين نوعهاي بنيادين بصورت صريح و ضمني
متغير
average
بصورت
double
(خط
55
از شكل 13-4) اعلان شده تا نتيجه اعشاري محاسبه انجام گرفته را در خود ذخيره سازد. با اين همه، متغيرهاي
total
و
gradeCounter
هر دو از نوع صحيح ميباشند. بخاطر داريد كه نتيجه تقسيم دو عدد صحيح يك عدد صحيح است كه در آن بخش اعشاري جواب از بين ميرود (قطع ميشود). در عبارت زير
average = total / gradeCounter;
ابتدا تقسيم انجام ميشود، از اينرو بخش اعشاري نتيجه قبل از تخصيص به
average
از بين ميرود. براي انجام يك محاسبه اعشاري با مقادير صحيح، بايستي مقادير موقتي كه اعداد اعشاري هستند براي محاسبه ايجاد كنيم. زبان
C++
داراي عملگر غيرباينري
cast
است كه اين وظيفه را انجام ميدهد. در خط
81
از عملگر
cast
بصورت
static_cast<double>(total)
براي ايجاد يك كپي موقت اعشاري از عملوند موجود در درون پرانتزها يعني
total
استفاده شده است. به استفاده از يك عملگر
cast
به اين روش، تبديل صريح ميگويند. هنوز مقدار ذخيره شده در
total
يك عدد صحيح است.
اكنون محاسبه متشكل از يك مقدار اعشاري (نسخه
double
موقت از
total
) است كه بر يك عدد صحيح در
gradeCounter
تقسيم ميشود. كامپايلر
C++
فقط از نحوه ارزيابي عباراتي كه در آن نوع دادههاي عملوندها يكسان هستند، اطلاع دارد. براي اطمينان از اينكه عملوندها از نوع مشابه هستند، كامپايلر مبادرت به انجام عملي بنام ترفيع كه تبديل ضمني نيز ناميده ميشود بر روي عملوندهاي انتخابي ميكند. براي مثال، در يك عبارت كه حاوي مقاديري از نوع داده
int
و
double
است،
C++
مبادرت به ترفيع عملوندهاي
int
به مقادير
double
ميكند. در اين مثال با
total
همانند يك نوع داده
double
رفتار ميكنيم (با استفاده از عملگر
cast
)، از اينرو كامپايلر مبادرت به ترفيع
gradeCounter
به
double
كرده و به محاسبه اجازه انجام ميدهد و نتيجه تقسيم اعشاري به
average
تخصيص مييابد. در فصل ششم، در مورد نوع دادههاي بنيادين و نحوه ترفيع آنها توضيح خواهيم داد.
خطاي برنامهنويسي
ميتوان از عملگر
cast
براي تبديل مابين نوعهاي بنيادين عددي همانند
int
و
double
و مابين نوع كلاسهاي مرتبط استفاده كرد (در فصل سيزدهم با اين موضوع آشنا خواهيد شد). تبديل به يك نوع اشتباه ميتواند خطاي كامپايلر يا خطاي زمان اجرا بوجود آورد.
عملگرهاي
cast
براي استفاده در هر نوع داده و همچنين نوعهاي كلاس در دسترس هستند. بدنبال عملگر
static_cast
يك جفت كاراكتر
>
و
<
كه نوع داده را احاطه كردهاند آورده ميشود. عملگر
cast
يك عملگر غيرباينري است. عملگري كه فقط يك عملوند اختيار ميكند. در فصل دوم، با عملگرهاي محاسباتي باينري آشنا شدهايد. همچنين
C++
از نسخههاي عملگرهاي غيرباينري جمع (
+
) و منفي (
-
) پشتيباني ميكند، از اينرو برنامهنويس ميتواند عبارتي مثل
-7
يا
+5
بنويسد. عملگر
cast
از ساير عملگرهاي غيرباينري همانند + و - از تقدم بالاتري برخوردار است. اين تقدم بالاتر از عملگرهاي
*
،
/
و
%
و پايينتر از پرانتز است. در جدول شكل 22-4 اين عملگر را با نماد
static_cast<type>()
عرضه كردهايم.
قالببندي اعداد اعشاري
قالببندي بكار رفته در برنامه شكل13-4 را بطور خلاصه در اين بخش و بطور دقيقتر در فصل پانزدهم توضيح خواهيم داد. فراخواني تابع
setprecision
در خط
86
(با آرگومان
2
) بر اين نكته دلالت دارد كه متغير
average
از نوع
double
بايستي با دو رقم معنيدار در سمت راست نقطه اعشار چاپ شود (مثلاً
97.37
) به اينحالت كنترلكننده جريان پارامتري شده (استريم) ميگويند (بدليل وجود
2
در درون پرانتز). برنامههايي كه از اين فراخواني استفاده ميكنند بايد حاوي رهنمود دستور دهنده زير باشند (خط
10
)
#include <iomanip>
خط
11
تصريحكننده نام فايل سرآيند
<iomanip>
است كه در اين برنامه بكار گرفته خواهد شد. دقت كنيد كه
endl
يك كنترلكننده جريان پارامتري نشده است (چرا كه پس از آن مقدار يا عبارتي در درون پرانتزها وجود ندارد) و نيازمند فايل سرآيند
<iomanip>
نيست. اگر دقت تعيين نشود، معمولاً اعداد اعشاري با شش رقم معنيدار چاپ ميشوند (دقت پيشفرض در اكثر سيستمهاي
32
بيتي). كنترلكننده جريان
fixed
بر اين نكته دلالت دارد كه مقادير اعشاري بايستي با خروجي كه فرمت نقطه ثابت ناميده ميشوند چاپ شوند، كه متضاد نماد علمي ميباشد. نماد علمي روشي براي نمايش يك عدد بصورت، عدد اعشاري مابين مقدار
1
الي
10
است كه در تواني از
10
ضرب ميشود. براي مثال، مقدار
3100
را ميتوان در نماد علمي بصورت
3.1×103
به نمايش در آورد. به هنگام نمايش مقاديري كه بسيار بزرگ يا بسيار كوچك هستند، نماد علمي ميتواند ابزار مناسبي براي اينكار باشد در فصل پانزدهم با قالببندي نماد علمي آشنا خواهيد شد. در طرف مقابل، قالببندي نقطه ثابت قرار دارد كه يك عدد اعشاري را مجبور ميكند تا به تعداد مشخص شده مبادرت به نمايش ارقام كند. همچنين اين فرمت نقطه اعشار و دنباله صفرها در چاپ را كنترل ميكند، حتي اگر عدد يك عدد صحيح باشد، همانند
88.00
، بدون قالببندي نقطه ثابت چنين عددي در
C++
بصورت
88
چاپ ميشود، بدون دنباله صفرها و نقطه اعشار. زمانيكه از كنترلكنندههاي جريان
fixed
و
setprecision
در برنامهاي استفاده ميشود، مقادير چاپ شده به تعداد نقاط ديسمال كه توسط مقدار ارسالي به
setprecision
مشخص ميشود، گرد ميشوند (همانند مقدار
2
در خط
86
)، اگرچه مقدار موجود در حافظه بدون تغيير باقي ميماند. براي مثال، مقادير
87.946
و
67.543
بصورت
87.95
و
67.54
چاپ ميشوند. توجه كنيد كه ميتوان نقطه اعشار را با استفاده از كنترلكننده جريان
showpoint
به نمايش درآورد. اگر
showpoint
بدون
fixed
بكار گرفته شود، دنباله صفحهها چاپ نخواهد شد. همانند
endl
، كنترلكنندههاي جريان
fixed
و
showpoint
پارامتري شده نبوده و نيازي به سرآيند فايل
<iomanip>
ندارند. هر دو آنها را ميتوان در سرآيند
<iostream>
پيدا كرد.
خط
86
و
87
از شكل 13-4 خروجي ميانگين كلاس هستند. در اين مثال ميانگين كلاس گرد شده به نزديكترين صدم و دقيقاً با دو رقم در سمت راست نقطه اعشار به نمايش درآمدهاند. كنترلكننده جريان پارامتري شده (خط
86
) نشان ميدهد كه مقدار متغير
average
بايستي با دقت دو رقم در سمت راست نقطه اعشار به نمايش درآيد
(setprecision(2))
. در اجراي نمونهاي برنامه سه نمره وارد برنامه 14-4 شده كه مجموع آنها
257
شده است و ميانگين حاصل از اين رقم عدد
85.666666
است. كنترلكننده جريان پارامتري شده
setprecision
سبب ميشود تا مقدار به تعداد رقم مشخص گرد شود. در اين برنامه، ميانگين به
85.67
گرد شده است.
10-4 فرموله كردن الگوريتمها: عبارات كنترلي تودرتو
اجازه دهيد تا به بررسي مسئله ديگري بپردازيم. مجدداُ الگوريتم را با استفاده از شبهكد و از بالا به پايين، اصلاح گام به گام فرموله كرده و سپس برنامه
C++
مربوط به آنرا خواهيم نوشت. در مثالهاي قبلي مشاهده كرديد كه عبارتهاي كنترل
ي
همانند يك پشته يكي بر روي ديگري و به ترتيب قرار داده ميشدند. در اين مرحله، به معرفي روشي خواهيم پرداخت كه عبارتهاي كنترلي در آن را ميتوان با يكديگر تركيب كرد، بطوريكه عبارتي در درون عبارت ديگر جاي ميگيرد.
به صورت مسئله توجه نمائيد:
يك كالج با برگزاري دورهاي دانشجويان را آماده امتحان پايان ترم ميكند. سال گذشته، 10 تن از دانشجويان كه اين دوره را گذرانده بودند در امتحان پايان ترم شركت كردند. مديريت كالج ميخواهد از وضعيت دانشجويان شركت كرده در امتحان مطلع شود. از شما خواسته شده تا برنامهاي بنويسيد تا خلاصهاي از نتايج آزمون ارائه دهد. ليستي از 10 دانشجو دريافت كرده و سپس در كنار نام كساني كه در آزمون قبول شدهاند
1
و كساني كه در آزمون مردود شدهاند
2
چاپ شود.
اين برنامه بايد بصورت زير نتايج آزمون را تحليل نمايد:
1- وارد كردن نتيجه هر آزمون (براي مثال
1
يا
2
). نمايش پيغام
“Enter result”
در هر بار كه برنامه درخواست نتيجه آزمون ميكند.
2- شمارش تعداد قبوليها و مردوديها.
3- نمايش خلاصهاي از نتايج آزمون، شامل تعداد دانشجويان كه موفق به گذراندن آزمون شدهاند و تعدادي كه مردود شدهاند.
4- اگر بيش از 8 دانشجواز آزمون با موفقيت عبور كردهاند. پيغام
“Raise tuition”
به نمايش درآيد.
پس از مطالعه صورت مسئله، تصميمات زير را براي حل آن اتخاذ ميكنيم:
1- برنامه بايد بر روي نتايج آزمون
10
دانشجو كار كند، از اينرو حلقه شمارنده - كنترل ميتواند بكار گرفته شود.
2- نتيجه هر آزمون عدد
1
يا
2
است. هر بار كه برنامه اقدام به خواندن نتيجه يك آزمون ميكند، برنامه بايد يك
1
يا
2
دريافت نمايد.
3- دو شمارنده به ذخيرهسازي نتايج آزمون ميپردازند. يكي براي شمارش تعداد دانشجويان كه از آزمون با موفقيت عبور كردهاند و ديگري براي شمارش تعدادي كه در آزمون مردود شدهاند.
4- پس از اينكه برنامه تمام نتايج را مورد پردازش قرار داد، بايد تعيين كند كه آيا تعداد قبوليها بيش از هشت نفر است يا خير.
اجازه دهيد تا با روش از بالا به پايين، اصلاح گامبهگام كار را دنبال كنيم. عبارت شبهكد زير در بالاترين سطح
(top)
قرار دارد:
Analyze exam result and decide if tuition should be raised
مجدداً يادآوري ميكنيم كه عبارت
top
توصيف كلي در مورد برنامه است و قبل از اينكه بتوان شبهكد را به فرم يك برنامه
C++
نوشت انجام چندين مرحله اصلاح گامبهگام ضروري است. اولين اصلاح عبارت است از:
Initialize variables
Input the 10 exam grades and count passes and failures
Print a summary of the exam result and decideif tuition should be raised
حتي زمانيكه يك تصور كامل از كل برنامه بدست آورده باشيم، انجام اصلاحات بعدي مورد نياز است. بايد به دقت به بررسي و مشخص كردن متغيرها پرداخت. شمارندهها به منظور ثبت قبوليها و مردوديها مورد نياز هستند. يك شمارنده، كنترل كننده حلقه بوده و يك متغير، ورودي كاربر را ذخيره ميكند. عبارت شبهكد زير
Initialize variables
ميتواند بصورت زير اصلاح شود:
Initialize passes to zero
Initialize failures to zero
Initialize student counter to one
فقط شمارندههاي، تعداد قبوليها و مردوديها و تعداد دانشآموزان مقداردهي اوليه ميشود. عبارت شبهكد
Input the 10 quiz grades and count passes and failures
مستلزم يك حلقه براي وارد كردن نتيجه هر آزمون است. در اين برنامه بدليل اينكه از همان ابتدا تعداد نتايج آزمون مشخص است (10)، از اينرو ميتوان از روش شمارنده-كنترل تكرار استفاده كرد. در درون حلقه يك عبارت انتخابي دو گانه تعيين ميكند كه نتيجه آزمون قبولي است يا مردودي و شمارنده مربوطه يك واحد افزايش مييابد. اصلاح عبارت شبهكد قبلي ميتواند بصورت زير انجام شود:
While student counter is less than or equal to 10
Prompt the user to enter the next exam result
Input the next exam result
If the student passed
Add one to passes
Else
Add one to failures
Add one to student counter
به نحوه استفاده از خطوط خالي در ميان مجموعه
if..else
دقت كنيد كه باعث افزايش خوانايي برنامه شده است. عبارت شبهكد
Print a summary of the exam results and decide if tuition should be raised
ميتواند بصورت زير اصلاح شود:
Print the number of passes
Print the number of failures
If more than eight student passed
Print “Raise tuition”
در شكل 15-4 دومين مرحله اصلاح بصورت كامل نشان داده شده است. به كاربرد خطوط خالي در ميان ساختار
while
توجه كنيد
، كه باعث افزايش خوانائي برنامه ميشوند
. اكنون اين شبهكد بقدر كافي براي تبديل به يك برنامه
C++
آماده شده است.
Initialize passes to zero
Initialize failures to zero
Initialize student to zero
While student counter is less than or equal to ten
Input the next exam result
If the student passed
Add one to passes
Else
Add one to failures
Add one to student counter
Print the number of passes
Print the number of failures
If more than eight students passed
شكل 15-4
|
شبهكد برنامه نتيجه آزمون.
تبديل به كلاس
Analysis
كلاس
C++
كه مبادرت به پيادهسازي الگوريتم شبهكد كرده است در شكلهاي 16-4 و 17-4 و دو اجراي نمونه در شكل 18-4 نشان داده است.
1
// Fig. 4.16: Analysis.h
2
// Definition of class Analysis that analyzes examination results.
3
// Member function is defined in Analysis.cpp
4
5
// Analysis class definition
6
class Analysis
7
{
8
public:
9
void processExamResults(); // process 10 students' examination results
10
}; // end class Analysis
شكل 16-4
|
برنامه بررسي نتيجه آزمون: فايل سرآيند
Analysis
.
1
// Fig. 4.17: Analysis.cpp
2
// Member-function definitions for class Analysis that
3
// analyzes examination results.
4
#include <iostream>
5
using std::cout;
6
using std::cin;
7
using std::endl;
8
9
// include definition of class Analysis from Analysis.h
10
#include "Analysis.h"
11
12
// process the examination results of 10 students
13
void Analysis::processExamResults()
14
{
15
// initializing variables in declarations
16
int passes = 0; // number of passes
17
int failures = 0; // number of failures
18
int studentCounter = 1; // student counter
19
int result; // one exam result (1 = pass, 2 = fail)
20
21
// process 10 students using counter-controlled loop
22
while ( studentCounter <= 10 )
23
{
24
// prompt user for input and obtain value from user
25
cout << "Enter result (1 = pass, 2 = fail): ";
26
cin >> result; // input result
27
28
// if...else nested in while
29
if ( result == 1 ) // if result is 1,
30
passes = passes + 1; // increment passes;
31
else // else result is not 1, so
32
failures = failures + 1; // increment failures
33
34
// increment studentCounter so loop eventually terminates
35
studentCounter = studentCounter + 1;
36
} // end while
37
38
// termination phase; display number of passes and failures
39
cout << "Passed " << passes << "\nFailed " << failures << endl;
40
41
// determine whether more than eight students passed
42
if ( passes > 8 )
43
cout << "Raise tuition " << endl;
44
} // end function processExamResults
شكل 17-4 |برنامه بررسي نتيجه آزمون: عبارات كنترلي تودرتو در فايل كد منبع
Analysis
1
// Fig. 4.18: fig04_18.cpp
2
// Test program for class Analysis.
3
#include "Analysis.h" // include definition of class Analysis
4
5
int main()
6
{
7
Analysis application; // create Analysis object
8
application.processExamResults(); // call function to process results
9
return 0; // indicate successful termination
10
} // end main
شكل 18-4
|
برنامه تست كننده كلاس
Analysis
خطوط
16-18
از برنامه17-4 متغيرهاي اعلان كردهاند كه تابع عضو
processExamResults
از كلاس
Analysis
از آنها براي پردازش نتايج آزمون استفاده ميكند. توجه كنيد كه از يكي از ويژگيهاي زبان
C++
استفاده كردهايم كه به مقداردهي اوليه متغير امكان ميدهد تا با بخش اعلان يكي شود (
passes
با صفر،
failures
با صفر و
studentCounter
با
1
مقداردهي اوليه شدهاند). امكان مقداردهي در ابتداي تكرار هر حلقه وجود دارد، معمولاً چنين مقداردهيهاي مجددي توسط عبارات تخصيصي بجاي اعلانها يا انتقال اعلانها بدرون بدنه حلقه صورت ميگيرند.
حلقه
while
ده بار تكرار ميشود (خطوط
22-36
). در هر تكرار، حلقه يك نتيجه آزمون دريافت و آن را پردازش ميكند. دقت كنيد كه عبارت
if..else
در خطوط
29-32
براي پردازش هر نتيجه در عبارت
while
بصورت تودرتو قرار گرفته است. اگر
result
برابر
1
باشد، عبارت
if..else
يك واحد به
passes
اضافه ميكند و در غير اينصورت فرض ميكند كه
result
برابر
2
بوده و يك واحد به
failures
اضافه مينمايد. خط
35
مبادرت به افزايش
studentCouunter
قبل از اينكه شرط تست حلقه در خط
22
صورت گيرد ميكند. پس دريافت
10
مقدار، حلقه پايان يافته و خط
39
تعداد قبوليها
(passes)
و مردوديها
(failures)
را به نمايش در ميآورد. عبارت
if
در خطوط
42-43
تعيين ميكند كه آيا تعداد دانشجويان قبول شده بيش از هشت نفر است يا خير. اگر چنين باشد پيغام
"Raise Tuition"
به نمايش در ميآيد.
بررسي كلاس
Analysis
در برنامه 18-4 يك شي
Analysis
ايجاد (خط
7
) و تابع عضو
processExamResults
فراخواني ميشود (خط
8
) تا مجموع نتايج آزمون وارد شده توسط كاربر پردازش شود. دو اجراي نمونه از برنامه 18-4 در خروجي نشان داده شده است. در انتهاي اولين اجرا، شرط موجود در خط
42
تابع عضو
procesExamResults
در شكل 17-4 برقرار شده (بيش از هشت دانشجو قبول شدهاند) و از اينرو پيغام
"Raise Tuition"
به نمايش درآمده است.
11-4 عملگرهاي تخصيصدهنده
C++
داراي چندين عملگر تخصيصدهنده براي كاستن از طول عبارات تخصيصي است. براي مثال، عبارت
c = c + 3;
ميتواند بصورت زير و با استفاده از عملگر
+=
نوشته شود
c += 3;
عملگر
+=
مقدار عملوند قرار گرفته در سمت راست را به مقدار عملوند سمت چپ اضافه كرده و نتيجه آنرا در متغير عملوند سمت چپ ذخيره ميكند. هر عبارتي به فرم زير را
;
عبارت عملگر متغير = متغير
ميتوان به فرم زير نوشت:
;
عبارت = عملگر متغير
عملگر يكي از عملگرهاي باينري +، -،
*
، / يا
%
و متغير يك مقدار سمت چپ
(lvalue)
است. مقدار سمت چپ، متغيري است كه در سمت چپ يك عبارت تخصيصي جاي ميگيرد. جدول شكل 19-4 حاوي عملگرهاي تخصيص دهنده محاسباتي و عبارات نمونهاي است كه از اين عملگرها استفاده ميكنند.
عملگر تخصيص دهنده
|
عبارت نمونه
|
معادل
|
تخصيص
|
با فرض
c = 3
،
d=5
،
e=4
،
f=6
و
g=12
|
+=
|
c += 7
|
c = c + 7
|
10
به
c
|
-=
|
d -= 4
|
d = d - 4
|
1
به
d
|
*=
|
e *= 5
|
e = e * 5
|
20
به
e
|
/=
|
f /= 3
|
f = f / 2
|
2
به
f
|
%=
|
g %= 9
|
g = g % 9
|
3
به
g
|
شكل 19-4 | عملگرهاي تخصيصدهنده
.
12
-
4 عملگرهاي افزاينده و كاهنده
علاوه بر عملگرهاي محاسباتي تخصيص دهنده، زبان
C++
داراي عملگر افزاينده غيرباينري
++
و عملگر كاهنده غيرباينري
--
است. اين عملگرها در جدول شكل 20-4 توضيح داده شدهاند. اگر متغير
c
بخواهد يك واحد افزايش پيدا كند، عملگر افزاينده
++
را ميتوان بجاي استفاده از عبارت
c = c + 1
يا
c += 1
بكار گرفت. اگر عملگر افزاينده يا كاهنده قبل از يك متغير قرار داده شود، به مفهوم پيشافزايش يافته يا پيشكاهش يافته خواهد بود. اگر عملگر افزاينده يا كاهنده پس از يك متغير بكار گرفته شود، به مفهوم پسافزايشي يا پسكاهشي خواهد بود.در هر دو حالت افزايشي يا كاهشي مقدار متغير يك واحد افزايش يا كاهش پيدا ميكند. پس از انجام كار، مقدار جديد متغير در عبارتي كه حاوي آن است بكار گرفته ميشود.
عملگر
|
عبارت نمونه
|
عنوان
|
توضيح
|
++
|
++a
|
پيشافزايشي
|
a
يك واحد افزايش مييابد، سپس مقدار جديد
a
در عبارتي كه
حاوي
a
است بكار گرفته ميشود.
|
++
|
a++
|
پسافزايشي
|
از مقدار جاري
a
در عبارتي كه حاوي آن است استفاده شده،
سپس مقدار
a
يك واحد افزايش مييابد.
|
--
|
--b
|
پيشكاهشي
|
b
يك واحد كاهش مييابد، سپس مقدار جديد
b
در عبارتي كه حاوي
b
است بكار گرفته ميشود.
|
--
|
b--
|
پسكاهشي
|
از مقدار جاري
b
در عبارتي كه حاوي آن است استفاده شده،
سپس مقدار
b
يك واحد كاهش مييابد.
|
شكل 20-4 | عملگرهاي افزاينده و كاهنده.
برنامه شكل21-4 به توصيف تفاوت موجود مابين نسخههاي پيشافزايش و پسافزايش عملگر افزاينده
++
ميپردازد. عملگر كاهنده
--
نيز به طريق مشابهي كار ميكند. توجه كنيد كه اين مثال حاوي كلاس نميباشد، اما فايل كد منبع با
main
تمام برنامهها كار ميكند. در اين فصل و فصل سوم شاهد مثالهاي بودهايد كه حاوي يك كلاس (شامل سرآيند و فايلهاي كد منبع براي اين كلاس بودهاند) به همراه فايل كد منبع ديگري براي تست كلاس بودند. اين فايل كد منبع حاوي تابع
main
است كه يك شي از كلاس ايجاد و توابع عضو خود را فراخواني ميكند. در اين مثال، فقط خواستهايم تا مكانيزم عملگر
++
را به نمايش در آوريم، از اينرو فقط از يك فايل كد منبع با تابع
main
استفاده كردهايم. خط
12
مبادرت به مقداردهي اوليه متغير
c
با
5
و خط
13
مقدار اوليه
c
را به نمايش در ميآورد. خط
14
مقدار عبارت
c++
را چاپ ميكند. اين عبارت سبب پسافزايش متغير
c
شده و در نتيجه مقدار اوليه
c
يعني
5
چاپ ميگردد، سپس مقدار
c
افزايش مييابد. از اينرو، خط
14
مقدار اوليه
c
يعني
5
را مجدداً چاپ ميكند. خط
15
مقدار جديد
c
يعني
6
را براي تاكيد بر اين نكته كه مقدار متغير براستي در خط
14
افزايش يافته است چاپ مينمايد.
1
// Fig. 4.21: fig04_21.cpp
2
// Preincrementing and postincrementing.
3
#include <iostream>
4
using std::cout;
5
using std::endl;
6
7
int main()
8
{
9
int c;
10
11
// demonstrate postincrement
12
c = 5; // assign 5 to c
13
cout << c << endl; // print 5
14
cout << c++ << endl; // print 5 then postincrement
15
cout << c << endl; // print 6
16
17
cout << endl; // skip a line
18
19
// demonstrate preincrement
20
c = 5; // assign 5 to c
21
cout << c << endl; // print 5
22
cout << ++c << endl; // preincrement then print 6
23
cout << c << endl; // print 6
24
return 0; // indicate successful termination
25
} // end main
شكل 21-4 |تفاوت مابين عملگرهاي
پيشافزايشي و پسافزايشي.
خط
20
مقدار متغير
c
را به
5
باز ميگرداند و خط
21
مقدار
c
را چاپ ميكند. خط
22
مبادرت به چاپ مقدار عبارت
++c
را ميكند. اين عبارت سبب پيشافزايش
c
شده است، از اينرو مقدار آن افزايش يافته و سپس مقدار جديد يعني
6
چاپ ميشود. خط
23
مجدداً مقدار
c
را به نمايش در ميآورد تا نشان دهد كه مقدار
c
هنوز پس از اجراي خط
22
برابر
6
است.
عملگرهاي تخصيص رياضي و عملگرهاي افزاينده و كاهنده ميتوانند عبارات برنامهنويسي را سادهتر كنند. سه عبارت تخصيصي در برنامه 17-4
passes = passes + 1;
failures = failures + 1;
studentCounter = studentCounter + 1;
را ميتوان با استفاده از عملگرهاي تخصيصي بصورت زير هم نوشت
passes += 1;
failures += 1;
student += 1;
با عملگرهاي پيشافزايشي بصورت زير
++passes;
++failures;
++studentCounter;
با عملگرهاي پسافزايشي بصورت زير نوشت
passes++
failures++;
studentCounter++;
خطاي برنامهنويسي
مبادرت به استفاده از عملگر افزاينده يا كاهنده بر روي عبارتي بجز نام يك متغير، همانند
++(x + 1)
خطاي نحوي خواهد بود.
جدول شكل 22-4 نمايشي از تقدم و ارتباط عملگرهاي مطرح شده تا بدينجا را عرضه كرده است. نمايش عملگرها با تقدم آنها از بالا به پايين است. ستون دوم توصيف كننده ارتباط عملگرها در هر سطح تقدم است. دقت كنيد كه عملگر شرطي
( ?: )
، عملگر غيرباينري پسافزايشي
( ++ )
، پسكاهشي
( - - )
، جمع
( + )
، تفريق
( - )
، و عملگرهاي
=
،
+=
،
*=
،
/=
و
%=
از چپ به راست ارزشيابي ميشوند. مابقي عملگرهاي جدول شكل 22-4 از راست به چپ ميباشند. ستون سوم اسامي عملگرها را نشان ميدهد.
عملگر
|
ارتباط
|
نوع
|
( )
|
چپ به راست
|
پرانتز
|
++ -- static_cast< type >()
|
چپ به راست
|
غيرباينري
|
++ -- + -
|
راست به چپ
|
غيرباينري
|
* / %
|
چپ به راست
|
تعددي
|
+ -
|
چپ به راست
|
افزاينده كاهنده
|
<<>>
|
چپ به راست
|
درج/استخراج
|
<<= >>=
|
چپ به راست
|
رابطهاي
|
== !=
|
چپ به راست
|
برابري
|
?:
|
راست به چپ
|
شرطي
|
= += -= *= /= %=
|
راست به چپ
|
تخصيصي
|
شكل 22-4 |تقدم و رابطه عملگرهاي مطرح شده تا اين مرحله.