This is Part 6 of the 7-part Exercise 6-0. Click here to see the other parts.
Exercise 6-0 (Part 6 / 7)
Relating to section 6.3.1 of the textbook (page 117-118). Implement and test out the “two-pass extract_fails” program. This program essentially split the students into two groups – failed and passed students. This version of extract_fails function uses remove_copy_if and erase from the <algorithm> directive. (Note that the original version of extract_fails function was first introduced in Chapter 5 / my Solution to Exercise 5-0.)
The Project
This section summarises the partitioned program in the form of C++ source and header files.

Source File List
Header File List
Source Files
main.cpp
#include <iostream> // std::cin, std::cout, std::endl
#include <string> // std::string
#include <vector> // std::vector
#include <algorithm> // std::sort
#include "Student_info.h" // Student_info, read
#include "extract_fails.h" // extract_fails
#include "grade.h" // grade
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::sort;
int main()
{
vector<Student_info> students;
Student_info record;
// read and store all the student's data.
while (read(cin, record))
students.push_back(record);
// Extract the failed students
vector<Student_info> students_failed = extract_fails(students);
// sort vectors
sort(students.begin(),students.end(),compare);
sort(students_failed.begin(),students_failed.end(),compare);
// Report passing students
cout << "These students have passed." << endl;
for (vector<Student_info>::const_iterator i = students.begin();
i != students.end(); ++i)
cout << i->name << " (" << grade(*i) << ")" << endl;
// Report failing students
cout << "These students have failed." << endl;
for (vector<Student_info>::const_iterator i = students_failed.begin();
i != students_failed.end(); ++i)
cout << i->name << " (" << grade(*i) << ")" << endl;
return 0;
}
extract_fails.cpp
#include <vector> // std::vector
#include <algorithm> // std::remove_copy_if
#include "Student_info.h"
#include "fgrade.h"
#include "pgrade.h"
using std::vector;
// A two-pass solution to extract the failed students
// (S6.3.1/117)
vector<Student_info> extract_fails(vector<Student_info>& students)
{
vector<Student_info> fail;
remove_copy_if(students.begin(), students.end(),
back_inserter(fail), pgrade);
students.erase(remove_if(students.begin(), students.end(), fgrade),
students.end() );
return fail;
}
fgrade.cpp
#include "Student_info.h" // Student_info
#include "grade.h" // grade
// predicate to determine whether a student failed
// (S5.1/75)
bool fgrade(const Student_info& s)
{
return grade(s) < 60;
}
grade.cpp
#include <stdexcept>
#include <vector>
#include "grade.h"
#include "median.h"
#include "Student_info.h"
using std::domain_error;
using std::vector;
// definitions for the grade functions from S4.1/52, S4.1.2/54, S4.2.2/63
// compute a student's overall grade from midterm and final exam
// grades and homework grade (S4.1/52)
double grade(double midterm, double final, double homework)
{
return 0.2 * midterm + 0.4 * final + 0.4 * homework;
}
// compute a student's overall grade from midterm and final exam grades
// and vector of homework grades.
// this function does not copy its argument, because median (function) does it for us.
// (S4.1.2/54)
double grade(double midterm, double final, const vector<double>& hw)
{
if (hw.size() == 0)
throw domain_error("student has done no homework");
return grade(midterm, final, median(hw));
}
// this function computes the final grade for a Student_info object
// (S4.2.2/63)
double grade(const Student_info& s)
{
return grade(s.midterm, s.final, s.homework);
}
median.cpp
// source file for the median function
#include <algorithm>
#include <stdexcept>
#include <vector>
using std::domain_error;
using std::sort;
using std::vector;
// compute the median of a vector<double>
// (S4.1.1/53)
double median(vector<double> vec)
{
typedef vector<double>::size_type vec_sz;
vec_sz size = vec.size();
if (size == 0)
throw domain_error("median of an empty vector");
sort(vec.begin(),vec.end());
vec_sz mid = size/2;
return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}
pgrade.cpp
#include "Student_info.h" // Student_info
#include "fgrade.h" // fgrade
// predicate to determine whether a student has passed (not failed)
// (S6.3.1/117)
bool pgrade(const Student_info& s)
{
return !fgrade(s);
}
Student_info.cpp
#include "Student_info.h"
using std::istream;
using std::vector;
// we are interested in sorting the Student_info object by the student's name
bool compare(const Student_info& x, const Student_info& y)
{
return x.name < y.name;
}
// read student's name, midterm exam grade, final exam grade, and homework grades
// and store into the Student_info object
// (as defined in S4.2.2/62)
istream& read(istream& is, Student_info& s)
{
// read and store the student's name and midterm and final exam grades
is >> s.name >> s.midterm >> s.final;
// read and store all the student's homework grades
read_hw(is, s.homework);
return is;
}
// read homework grades from an input stream into a vector<double>
// (as defined in S4.1.3/57)
istream& read_hw(istream& in, vector<double>& hw)
{
if (in)
{
// get rid of previous contents
hw.clear();
// read homework grades
double x;
while (in >> x)
hw.push_back(x);
// clear the stream so that input will work for the next student
in.clear();
}
return in;
}
Header Files
extract_fails.h
#ifndef GUARD_EXTRACT_FAILS_H #define GUARD_EXTRACT_FAILS_H // extract_fails.h #include <vector> #include "Student_info.h" std::vector<Student_info> extract_fails(std::vector<Student_info>&); #endif // GUARD_EXTRACT_FAILS_H
fgrade.h
#ifndef GUARD_FGRADE_H #define GUARD_FGRADE_H // fgrade.h #include "Student_info.h" // Student_info #include "grade.h" // grade bool fgrade(const Student_info&); #endif // GUARD_FGRADE_H
grade.h
#ifndef GUARD_GRADE_H #define GUARD_GRADE_H // grade.h #include <vector> #include "Student_info.h" double grade(double, double, double); double grade(double, double, const std::vector<double>&); double grade(const Student_info&); #endif // GUARD_GRADE_H
median.h
#ifndef GUARD_MEDIAN_H #define GUARD_MEDIAN_H // median.h #include <vector> double median(std::vector<double>); #endif // GUARD_MEDIAN_H
pgrade.h
#ifndef GUARD_PGRADE_H #define GUARD_PGRADE_H // pgrade.h #include "Student_info.h" // Student_info bool pgrade(const Student_info&); #endif // GUARD_PGRADE_H
Student_info.h
#ifndef GUARD_STUDENT_INFO_H
#define GUARD_STUDENT_INFO_H
// Student_info.h
#include <iostream>
#include <string>
#include <vector>
struct Student_info
{
std::string name;
double midterm, final;
std::vector<double> homework;
};
bool compare(const Student_info&, const Student_info&);
std::istream& read(std::istream&, Student_info&);
std::istream& read_hw(std::istream&, std::vector<double>&);
#endif // GUARD_STUDENT_INFO_H
Test Program
I now provide a set of student grades. The program separate the students to the pass and fail groups as expected.
pete 100 100 100 100 100 jon 90 90 0 0 0 mary 50 50 50 50 50 anna 40 40 0 0 0 gary 80 80 0 80 0 bob 100 100 100 0 0 ken 20 88 99 44 66 jay 99 39 40 80 0 bill 20 88 0 39 0 ^Z ^Z These students have passed. bob (60) ken (65.6) pete (100) These students have failed. anna (24) bill (39.2) gary (48) jay (51.4) jon (54) mary (50)