C++ برای ذخیرهی «عنصر»های همنوع به صورت مرتب، «ظرف»های مختلفی ارائه میدهد. std::array برای ظرفهایی با اندازهی ثابت وجود دارد و std::vector که قابلیت تغییر اندازهی پویا دارد.
وقتی یک «آرایه» یا «بردار» تعریف میکنید، باید نوع عناصری را مشخص کنید که آن ظرف نگه میدارد. آرایه هم به یک اندازه نیاز دارد. برای دیدن نحوهی مقداردهی اولیهی این دو نوع ظرف، به این مثالها نگاه کنید:
#include <array>
#include <string>
// std::array<element_type, size> variable_name {list of elements}
std::array<std::string, 3> indie_rock {"yeah", "yeah", "yeah"};
// indie_rock contains the elements "yeah" three times
بردار معمولاً به فضای بیشتری نیاز دارد، چون برای رشد بیشتر حافظه اختصاص میدهد. لازم نیست اندازه را مشخص کنید:
#include <vector>
// std::vector<element_type> variable_name {list of elements}
std::vector<int> countdown {3, 2, 1};
// countdown contains the elements 3, 2 and 1
نکته: لازم نیست مکانیزم دقیق پشت مفهوم
templateرا از الان بدانید؛ این مفهوم پایینتر در درخت برنامهی درسی، بخش خودش را خواهد داشت.
بردار و آرایه توابع یکسانی برای دسترسی به عناصرشان دارند. میتوانید از توابع عضو front و back برای به دست آوردن اولین و آخرین عنصر ظرف استفاده کنید. همچنین at و عملگر [] برای دسترسی به عناصر مشخص وجود دارند.
countdown[0];
// => 3
countdown.at(2);
// => 1
countdown[1] = 4;
// countdown now contains 3, 4 and 1
indie_rock.back() = "yeahs";
// indie_rock is now "yeah", "yeah", "yeahs"
نکته: اگر موقعیتی را انتخاب کنید که در محدودهی ظرف شما نباشد،
[]ممکن است به رفتار تعریفنشده منجر شود. تابعatیک استثنا ایجاد میکند که در درازمدت میتواند کار شما را راحتتر کند.
از آنجا که اندازهی vector ثابت نیست، میتوان عنصر اضافه یا حذف کرد. دو تابع رایج برای این کار emplace_back و pop_back هستند.
std::vector<std::string> witches {"Holly", "Alyssa", "Shannen"};
witches.pop_back();
// Shannen is no longer with the witches
witches.emplace_back("Rose");
// Rose has joined the team
میتوانید خالی بودن ظرفها را با تابع عضو empty بررسی کنید. اگر میخواهید تعداد عناصر را بدانید، میتوانید از size استفاده کنید.
شما یک دستیار آموزشی هستید که برگههای امتحان دانشآموزان را تصحیح میکند. پیگیری دستی نتایج هم خستهکننده شده و هم بهسادگی اشتباه رخ میدهد. تصمیم میگیرید با ساختن چند تابع برای شمردن و محاسبهی نتایج کلاس، ماجرا را کمی جالبتر کنید.
میتوانید برای پرسشهای امتحان «نمرهی جزئی» در نظر بگیرید، اما نمرههای نهایی امتحان باید از نوع int باشند.
پس پیش از هر کار دیگری با نمرههای کلاس، باید نمرهها را مرور کنید و هر نمرهی double را به int تبدیل کنید.
تابع round_down_scores() را بسازید که یک vector از student_scores میگیرد.
گرد کردنی که مدرسهها خواستهاند، حذف بخش اعشاری است.
خوشبختانه، C++ میتواند یک نوع را با static_cast به نوع دیگری تبدیل کند.
تبدیل static_cast از double به int هر رقمی را که پس از ممیز بیاید حذف میکند.
این تابع باید vector ورودی را بگیرد و یک vector جدید return کند که همهی نمرههایش به int تبدیل شده باشند.
std::vector<double> student_scores {90.33, 40.5, 55.44, 70.05, 30.55, 25.45, 80.45, 95.3, 38.7, 40.3};
round_down_scores(student_scores)
// => {90, 40, 55, 70, 30, 25, 80, 95, 38, 40}
روشهای دیگری هم هستند که همین نتیجه را به دست میدهند، اما تبدیل نوع چیز رایجی است.
int new_PI{static_cast<int>(3.14) + static_cast<int>(0.001592653589)};
// new_PI is 3.
هنگام تصحیح امتحان متوجه شدید که عملکرد بعضی دانشآموزان آنقدر که امید داشتید خوب نبود. اما حواستان پرت شد و فراموش کردید که دقیقاً چند دانشآموز را یادداشت کنید.
تابع count_failed_students() را بسازید که یک vector از student_scores میگیرد.
این تابع باید تعداد دانشآموزانی را که نمرهی قبولی ندارند بشمارد و آن تعداد را بهصورت یک عدد صحیح برگرداند.
یک دانشآموز برای گرفتن نمرهی قبولی در امتحان به نمرهای بزرگتر از ۴۰ نیاز دارد.
count_failed_students({90,40,55,70,30,25,80,95,38,40});
// => 5
معلمی که به او کمک میکنید دوست دارد علاوه بر نمرههای عددی، نمرهی حرفی هم بدهد. از آنجا که دانشآموزان بهندرت در امتحان نمرهی ۱۰۰ میگیرند، حدهای پایین «نمرهی حرفی» بر پایهی بالاترین نمرهی کسبشده محاسبه میشود و بین بالاترین نمره و حد قبولنشدن، یعنی <= ۴۰، بهطور یکنواخت افزایش مییابد.
تابع letter_grades() را بسازید که بالاترین نمرهی امتحان را بهعنوان پارامتر میگیرد و یک array (نه vector) از حدهای پایین نمره برای هر بازهی نمرهی «سبک آمریکایی» برمیگرداند: ["D", "C", "B", "A"].
/*Where the highest score is 100, and failing is <= 40.
"F" <= 40
41 <= "D" <= 55
56 <= "C" <= 70
71 <= "B" <= 85
86 <= "A" <= 100
*/
letter_grades(100);
// => {41, 56, 71, 86}
/*Where the highest score is 88, and failing is <= 40.
"F" <= 40
41 <= "D" <= 52
53 <= "C" <= 64
65 <= "B" <= 76
77 <= "A" <= 88
*/
letter_grades(88);
// => {41, 53, 65, 77}
نمرههای امتحان را به ترتیب نزولی در اختیار دارید، همراه با اسم دانشآموزان مربوطه (که بر اساس ترتیب نمرههایشان مرتب شدهاند). میخواهید اسم هر دانشآموز را با نمرهی امتحانش تطبیق دهید و رتبهبندی کلی کلاس را چاپ کنید.
تابع student_ranking() را با پارامترهای student_scores و student_names بسازید.
اسم هر دانشآموز را در vector مربوط به student_names با نمرهی او در vector مربوط به student_scores تطبیق دهید.
میتوانید فرض کنید که هر vector آرگومان از بالاترین نمره به پایینترین نمره مرتب شده است.
تابع باید یک vector از رشتهها با قالب <rank>. <student name>: <student score> برگرداند.
std::vector<int> student_scores {100, 99, 90, 84, 66, 53, 47};
std::vector<std::string> student_names {"Joci", "Sara","Kora","Jan","Indra","Bern", "Fred"};
student_ranking(student_scores, student_names)
// =>
// {"1. Joci: 100", "2. Sara: 99", "3. Kora: 90", "4. Jan: 84", "5. Indra: 66", "6. Bern: 53", "7. Fred: 47"}
هرچند نمرهی «کامل» ۱۰۰ در امتحان کمنظیر است، دانستن اینکه آیا دستکم یک دانشآموز آن را کسب کرده یا نه جالب است.
تابع perfect_score() را با پارامترهای student_scores و student_names بسازید.
فهرستها همان فهرستهای تمرین ۴ هستند.
تابع باید اولین <name> (بهصورت رشته) از دانشآموزی را return کند که در امتحان نمرهی ۱۰۰ گرفته است.
اگر هیچ نمرهی ۱۰۰ پیدا نشد، باید یک رشتهی خالی "" برگردانده شود.
perfect_score({"Nur", "Tony", "Fatima"}, {90, 80, 100});
// => "Fatima"
perfect_score({"Nur", "Tony"}, {90, 80});
// => ""