Exercise 6-7
The portion of the grading analysis program from S6.2.1/110 that read and classified student records depending on whether they did (or did not) do all the homework is similar to the problem we solved in extract_fails. Write a function to handle this subproblem.
Solution
The “one-pass” extract_fail program (as per S6.3.2/119 of the textbook) takes in a vector<Student_info> container, extracts out the failed students, and leave the passed students in that input vector<Student_info> container. The objective of this exercise is to reuse this concept on a somewhat scenario. i.e. to takes in the same vector<Student_info> container, extracts out the students who didn’t did all the homework, and leave the students who did the homework in that input vector<Student_info> container – we can build a function called (say) extractDidnt that builds on the extract_fail program.
The Skeleton extract_fail function
The original “one-pass” extract_fail function looks like this:
#include <vector> // std::vector
#include <algorithm> // std::remove_copy_if
#include "Student_info.h"
#include "fgrade.h"
#include "pgrade.h"
using std::vector;
// A single-pass solution to extract the failed students
// (S6.3.2/119)
vector<Student_info> extract_fails(vector<Student_info>& students)
{
vector<Student_info>::iterator iter =
stable_partition(students.begin(), students.end(), pgrade);
vector<Student_info> fail(iter, students.end());
students.erase(iter, students.end());
return fail;
}
The implementation does the following:

- Process directly an input vector<Student_info> students container. Uses stable_parition to put the passed students to the front of that vector – with the help of its third argument pgrade as a predicate.
- Instantiate a vector<Student_info> fail container to include only the failed students. We return this container at the end.
- Erase those failed students from the original input vector<Student_info> students container, essentially keeping only the passed students.
The end result is, a new vector<Student_info> fail container that contains only the failed students, and a “reduced” vector<Student_info> students container that now contains only the passed Students.
The new extractDidnt function
Applying the same concept our new extractDidnt function may look like the following.
#include <vector> // std::vector
#include <algorithm> // std::remove_copy_if
#include "Student_info.h"
#include "did_all_hw.h"
using std::vector;
// A single-pass solution to extract the students who didn't do all homework
// adjusted version of S6.3.2/119) - for exercise 6-7
vector<Student_info> extractDidnt(vector<Student_info>& students)
{
vector<Student_info>::iterator iter =
stable_partition(students.begin(), students.end(), did_all_hw);
vector<Student_info> didnt(iter, students.end());
students.erase(iter, students.end());
return didnt;
}
The implementation does the following:

- Process directly an input vector<Student_info> students container. Uses stable_parition to put the “did-all-homework” students to the front of that container – with the help of its third argument did_all_hw as a predicate.
- Instantiate a new vector<Student_info> didnt container to include only the “did-not-do-all-homework” students. We return this container at the end.
- Erase those “did-not-do-all-homework” students from the original input vector<Student_info> students container, essentially keeping only the passed students.
The end result is, a new vector<Student_info> didnt container that contains only the “did-not-do-all-homework” students, and a “reduced” vector<Student_info> students container that now contains only the “did-all-homework” Students.
The Project
I now wrap the entire projects here, including all the source and header files used. Note that this project is built on the Project within my Solution to Exercise 6-6. (i.e. I have merely amended the main.cpp file, and added the extractDidnt.cpp file).

Source File List
- main.cpp
- average.cpp
- average_grade.cpp
- did_all_hw.cpp
- doAnalysis.cpp
- extractDidnt.cpp
- grade.cpp
- grade_aux.cpp
- median.cpp
- optimistic_median.cpp
- Student_info.cpp
- write_analysis.cpp
Header File List
- average.h
- average_grade.h
- did_all_hw.h
- doAnalysis.h
- extractDidnt.h
- grade.h
- grade_aux.h
- median.h
- optimistic_median.h
- Student_info.h
- write_analysis.h
Source Files
main.cpp
#include <iostream> // cin, cout, endl
#include <vector> // vector
#include "Student_info.h" // Student_info
#include "extractDidnt.h" // extractDidnt
#include "write_analysis.h" // write_analysis
#include "grade_aux.h" // grade_aux
#include "average_grade.h" // average_grade
#include "optimistic_median.h" // optimistic_median
using std::cin;
using std::cout;
using std::endl;
using std::vector;
int main()
{
// read the student records
Student_info student;
vector<Student_info> did; // to store students who did all homework
while (read(cin, student))
did.push_back(student);
// extract out the students who didnt do all homework
vector<Student_info> didnt = extractDidnt(did);
// verify that the analyses will show us something
if (did.empty()) {
cout << "No student did all the homework!" << endl;
return 1;
}
if (didnt.empty()) {
cout << "No student did all the homework!" << endl;
return 1;
}
// do the analyses
write_analysis(cout, "median", grade_aux, did, didnt);
write_analysis(cout, "average", average_grade, did, didnt);
write_analysis(cout, "median of homework turned in",
optimistic_median, did, didnt);
return 0;
}
average.cpp
#include <vector> // vector
#include <numeric> // numeric
using std::vector;
using std::accumulate;
// Compute average of elements
// (S6.2.3/115)
double average(const vector<double>& v)
{
return accumulate(v.begin(), v.end(), 0.0) / v.size();
}
average_grade.cpp
#include "Student_info.h" // Student_info
#include "grade.h" // grade
#include "average.h" // average
// Compute the final grade using average of homework
// (S6.2.3/115)
double average_grade(const Student_info& s)
{
return grade(s.midterm, s.final, average(s.homework));
}
did_all_hw.cpp
#include <algorithm> // find
#include "Student_info.h" // Student_info
// Has the student done all the homework?
// (S6.2.1/110)
bool did_all_hw(const Student_info& s)
{
return ((find(s.homework.begin(), s.homework.end(), 0)) == s.homework.end());
}
doAnalysis.cpp
#include <vector> // vector
#include <algorithm> // transform
#include "Student_info.h" // Student_info
#include "median.h" // median
using std::vector;
using std::transform;
// Exercise 6-6: a consolidated auxiliary function
double doAnalysis(const vector<Student_info>& students,
double useGradeScheme(const Student_info&))
{
vector<double> grades;
transform(students.begin(), students.end(),
back_inserter(grades), useGradeScheme);
return median(grades);
}
extractDidnt.cpp
#include <vector> // std::vector
#include <algorithm> // std::remove_copy_if
#include "Student_info.h"
#include "did_all_hw.h"
using std::vector;
// A single-pass solution to extract the students who didn't do all homework
// adjusted version of S6.3.2/119) - for exercise 6-7
vector<Student_info> extractDidnt(vector<Student_info>& students)
{
vector<Student_info>::iterator iter =
stable_partition(students.begin(), students.end(), did_all_hw);
vector<Student_info> didnt(iter, students.end());
students.erase(iter, students.end());
return didnt;
}
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);
}
grade_aux.cpp
#include "Student_info.h" // Student_info
#include "grade.h" // grade
#include <stdexcept> // domain_error
using std::domain_error;
// Auxiliary function to be parsed as argument to a function
// (S6.2.2/113)
double grade_aux(const Student_info& s)
{
try {
return grade(s);
} catch (domain_error) {
return grade(s.midterm, s.final, 0);
}
}
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>
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];
}
optimistic_median.cpp
#include <vector> // vector
#include <algorithm> // remove_copy, back_inserter
#include "Student_info.h" // Student_info
#include "grade.h" // grade
#include "median.h" // median
using std::vector;
// median of the nonzero elements of s.homework, or 0 if no such elements exist
double optimistic_median(const Student_info& s)
{
vector<double> nonzero;
remove_copy(s.homework.begin(), s.homework.end(),
back_inserter(nonzero), 0);
if (nonzero.empty())
return grade(s.midterm, s.final, 0);
else
return grade(s.midterm, s.final, median(nonzero));
}
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;
}
write_analysis.cpp
#include <iostream> // ostream, endl;
#include <vector> // vector
#include <string> // string
#include "Student_info.h" // Student_info
#include "doAnalysis.h" // doAnalysis
using std::ostream;
using std::endl;
using std::string;
using std::vector;
// Output result to compare the two groups of students who did and
// who didn't do all of their homework.
// (S6.2.3/113) | updated for Exercise 6-6
void write_analysis(ostream& out,
const string& name,
double useGradeScheme(const Student_info&),
const vector<Student_info>& did,
const vector<Student_info>& didnt)
{
out << name << ": median(did) = " << doAnalysis(did, useGradeScheme) <<
": median(didnt) = " << doAnalysis(didnt, useGradeScheme) <<
endl;
return;
}
Header Files
average.h
#ifndef GUARD_AVERAGE_H #define GUARD_AVERAGE_H // average.h #include <vector> double average(const std::vector<double>&); #endif // GUARD_AVERAGE_H
average_grade.h
#ifndef GUARD_AVERAGE_GRADE_H #define GUARD_AVERAGE_GRADE_H // average_grade.h #include "Student_info.h" // Student_info double average_grade(const Student_info&); #endif // GUARD_AVERAGE_GRADE_H
did_all_hw.h
#ifndef GUARD_DID_ALL_HW_H #define GUARD_DID_ALL_HW_H // did_all_hw.h #include "Student_info.h" // Student_info bool did_all_hw(const Student_info&); #endif // GUARD_DID_ALL_HW_H
doAnalysis.h
#ifndef GUARD_DOANALYSIS_H
#define GUARD_DOANALYSIS_H
#include <vector>
#include "Student_info.h"
double doAnalysis(const std::vector<Student_info>&,
double useGradeScheme(const Student_info&));
#endif // GUARD_DOANALYSIS_H
extractDidnt.h
#ifndef GUARD_EXTRACTDIDNT_H #define GUARD_EXTRACTDIDNT_H #include <vector> // vector #include "Student_info.h" // Student_info std::vector<Student_info> extractDidnt(std::vector<Student_info>&); #endif // GUARD_EXTRACTDIDNT_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
grade_aux.h
#ifndef GUARD_GRADE_AUX_H #define GUARD_GRADE_AUX_H // grade_aux.h #include "Student_info.h" // Student_info double grade_aux(const Student_info&); #endif // GUARD_GRADE_AUX_H
median.h
#ifndef GUARD_MEDIAN_H #define GUARD_MEDIAN_H // median.h #include <vector> double median(std::vector<double>); #endif // GUARD_MEDIAN_H
optimistic_median.h
#ifndef GUARD_OPTIMISTIC_MEDIAN_H #define GUARD_OPTIMISTIC_MEDIAN_H // optimistic_median.h #include "Student_info.h" // Student_info double optimistic_median(const Student_info&); #endif // GUARD_OPTIMISTIC_MEDIAN_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
write_analysis.h
#include <iostream> // ostream;
#include <vector> // vector
#include <string> // string
#include "Student_info.h" // Student_info
void write_analysis(std::ostream&,
const std::string&,
double useGradeScheme(const Student_info&),
const std::vector<Student_info>&,
const std::vector<Student_info>&);
#endif // GUARD_WRITE_ANALYSIS_H
Test Program
I now submit the same input (as the test to the original program / Solution to Exercise 6-0 (Part 5 / 7)). The test shows that we get the same result, implying the code updates are not causing observable issues.
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 median: median(did) = 65.6: median(didnt) = 49.7 average: median(did) = 67.0667: median(didnt) = 52.7 median of homework turned in: median(did) = 65.6: median(didnt) = 57.1