整理考试成绩

概念学习C++17

简介

数组与动态数组

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

注意:现在不必掌握模板的具体机制,后续课程会专门介绍。

访问元素

动态数组和数组具有相同的元素访问方式。使用成员函数 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 获取元素数量。

任务说明

你是一名助教,负责批改学生试卷。手动记录成绩既繁琐又容易出错,于是决定编写一些函数,统计和计算全班成绩,让这项工作更有趣。

1. 成绩取整

单道题可以给部分分,但考试总成绩必须是 int。因此,在进行其他统计前,需要遍历成绩,把 double 成绩转换为 int。

创建 round_down_scores() 函数,接收 student_scores 动态数组。学校要求直接截去小数部分。C++ 可以用 static_cast 转换类型,double 转为 int 时会去掉小数点后的数字。函数应返回一个新的动态数组,其中所有成绩都已转换为 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.

2. 统计不及格人数

批改时,你注意到一些学生的表现不如预期,但当时分了神,忘了记录具体有多少人。

创建 count_failed_students() 函数,接收 student_scores 动态数组,统计未达到及格要求的学生人数,并以整数返回。考试成绩必须大于 40 分才算及格。

count_failed_students({90,40,55,70,30,25,80,95,38,40});
// => 5

3. 计算字母等级

你协助的教师喜欢同时给出数字成绩和字母等级。由于学生很少得到 100 分,字母等级的下界要根据本次最高分确定,并在最高分与不及格阈值 ≤40 之间均匀划分。

创建 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}

4. 将姓名与成绩对应

现有按成绩降序排列的分数,以及按相同顺序排列的学生姓名。你希望将姓名与分数对应,输出全班排名。

创建 student_ranking() 函数,接收 student_scores 和 student_names。将姓名动态数组中的每个学生与分数动态数组中的对应成绩匹配。可以假设两个数组都已按成绩从高到低排列。返回字符串动态数组,每项格式为 <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"}

5. 查找满分学生

尽管考试中很少有人得到 100 分,知道是否至少有一人满分仍然很有意义。

创建 perfect_score() 函数,接收 student_scores 和 student_names,使用与任务 4 相同的数组。返回第一个得到 100 分的学生姓名,类型为字符串。

如果没有满分,返回空字符串 ""。

perfect_score({"Nur", "Tony", "Fatima"}, {90, 80, 100});
// => "Fatima"

perfect_score({"Nur", "Tony"}, {90, 80});
// => ""
需要一点帮助?查看分步提示

提示

通用提示

  • while 用于次数不确定的重复执行。
  • for 常用于次数明确的循环。
  • break 和 continue 可以调整循环行为。
  • 字符串库中的 std::to_string() 可以把整数转换为字符串。

熟悉下列操作也有助于完成任务:

1. 成绩取整

  • while 在测试条件变为 false 之前持续执行。
  • <vector>.back() 返回最后一项。
  • <vector>.pop_back() 删除最后一项。

2. 统计不及格人数

  • 设置一个计数器,遇到不及格成绩时递增。循环结束后返回这个计数。

3. 计算字母等级

  • 不及格是 ≤40,因此最低及格分始终是 41。应把从 41 到最高分的剩余区间均分为四段,每段对应一个字母等级。
  • 最高分减去 40,得到 A 至 D 覆盖的分数跨度。
  • 再除以 4,得到每个等级的区间大小。
  • 从 40 开始逐次累加这个大小,推导各等级的下界,并注意整数边界。
  • 可以使用 static_cast<int> 截去计算结果的小数部分。
  • 返回的容器必须是数组,而不是动态数组。

4. 将姓名与成绩对应

  • 如果两个容器长度相同、排序方式相同,是否可以用一个容器的下标获取另一个容器的值?
  • std::to_string(int) 可以将数字转换为字符串。
  • 不要忘记遵循示例输出格式。

5. 查找满分学生

  • 可能有满分学生,也可能没有;必须检查完相关成绩,才能判断应返回空字符串。
  • 可以考虑用 continue 和 break 跳过不需要的值,或结束查找。