Solution to Programming Assignment 4
Using Arrays
This solution to program 4 is not the one we expected anyone to turn in. It's provided to illustrate the use of arrays in a program with several functions. It's a good illustration of how a more general program can sometimes be shorter and easier to write than a more specific one. I.e., this array solution is more general than the non-array solution in that allows grade files with an arbitrary number of of graded items, rather than a maximum of five items. The array solution is over 100 lines shorter than the non-array solution, and the array solution is conceptually clearer, as long as you understand how arrays work.
////
//
// This program computes statistics for student scores stored in a grades file.
// The file contains header information describing the graded items for a
// class. The information for each graded item consists of its label, (e.g.,
// "midtm", "final") and the percentage of the total score for that item.
// Following the header information are individual student records containing
// student name, short four-digit id, and raw scores for each graded item.
//
// The program begins execution by prompting for the name of the grades file
// from the terminal. If the file opens successfully, the program computes the
// statistical information and outputs it to the terminal. The output is in
// the form of a table with the following labeled columns: student name,
// student id, scores for each graded item, and weighted total score. The
// weighted total is the sum of each score multiplied by its percentage. Each
// row in the table contains the information for one student.
//
// At the end of the output table are two rows of computed statistics for each
// scores column, as well as for the total column. The first statistics row
// has the means of the scores, the second row has the standard deviations.
//
// Further details of program operation, including precise input and output
// formats, are available in the program specification at
//
// http://www.csc.calpoly.edu/~gfisher/classes/101/assignments/program4.html
//
// NOTE: The specification stipulates that the program can process grades files
// with up to five graded items. This program extends the specification by
// allowing files with any number of graded items to be processed.
//
//
// Author: Gene Fisher (gfisher@calpoly.edu)
// Created: 13apr99
// Modified: 13may99
//
////
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <math.h>
////
//
// Function OpenGradesFile prompts the user for a file name from the terminal
// and opens a file of that name. The opened file is returned in the reference
// parameter grades_file.
//
////
void OpenGradesFile(
ifstream& grades_file // Opened file
);
////
//
// Function ProcessHeaderInfo reads header information from the given grades
// file, except for the first value. (The first value is the number of graded
// items, and it is read in main to determine the size of processing arrays,
// including the percents array sent to this function.) ProcessHeaderInfo
// returns the number of items graded so far and the percentage values for the
// graded items. These returns are through the reference parameters.
// ProcessHeaderInfo uses three subfunctions to perform its computation:
// InputAndInitCounters, ProcessItemInfo, and OutputRestOfColumnHeading.
// Further processing details are described in the declarations of these
// functions below.
//
////
void ProcessHeaderInfo(
ifstream& grades_file, // Input file
int num_items, // Number of graded items
int& num_graded, // Number of items graded so far
int percents[] // Percentages each graded item
);
////
//
// Function InputAndInitCounters reads the first two numbers from the given
// grades file, which are the number of graded items and the number of items
// graded so far. These values are returned in the num_items and num_graded
// reference parameters. The item_counter reference parameter is initialized
// to the value of num_items. This counter is used subsequently by later
// functions to control file processing.
//
////
void InputAndInitCounters(
ifstream& grades_file, // Input file
int num_items, // Number of graded items
int& num_graded, // Number of items graded so far
int& item_counter // Counter for reading item info
);
////
//
// Function ProcessItemInfo reads a label and percentage value from the given
// grades file. If there is further item information to read, the grades_file
// read marker is assumed to be at the beginning of a line containing the
// information. The label read from the file is output immediately to the
// terminal. The percentage value is returned in the percent reference
// parameter.
//
////
void ProcessItemInfo(
ifstream& grades_file, // Input file
int& percent // Percentage of the processed graded item
);
////
//
// Function OutputRestOfColumnHeading inputs and discards the "======" heading
// separator from the given grades file. It outputs to the terminal the
// "TOTAL" column heading and a line containing enough equals signs to extend
// from column 1 to to just below the "L" in the TOTAL column label. The
// num_items input is used to compute the proper number of equals signs to
// output.
//
////
void OutputRestOfColumnHeading(
ifstream& grades_file, // Input file
int num_items // Number of graded items
);
////
//
// Function ProcessStudentInfo reads the lines of student grade information
// from the given grades file. As the information is read, the raw score
// values and weighted totals are output to the terminal. The num_items and
// num_graded input parameters are used to control how many items are processed
// for each student. The percent inputs are used to compute the weighted
// total score for each student.
//
// The sums and sum_sqs reference parameters are used to return the sums and
// sums of squares for all students for each graded item as well as for the
// weighted totals. ProcessStudentInfo uses three subfunctions to perform its
// computation: InitStatsVars, ProcessStudentNameAndId, and
// ProcessStudentScores. Further processing details are described in the
// declarations of these functions below.
//
////
void ProcessStudentInfo(
ifstream& grades_file, // Input file
int num_items, // Number of graded items
int num_graded, // Number of items graded so far
int& num_students, // Total number of students
int percents[], // Percentages for each graded item
float sums[], // Column sums for each item plus TOTAL
float sum_sqs[] // Sum of squares for each item plus TOTAL
);
////
//
// Function InitStatsVars initializes all of the given reference parameters to
// zero, in preparation for the computations performed in the
// ProcessStudentScores function.
//
////
void InitStatsVars(
int& num_students, // Total number of students
int num_items, // Number of graded items (arrays upper bound)
float sums[], // Column sums for each item plus TOTAL
float sum_sqs[] // Sum of squares for each item plus TOTAL
);
////
//
// Function ProcessStudentNameAndId outputs the student name and id to the
// terminal, with proper spacing. The name is received as an input parameter,
// having been read in the calling function. (The calling function reads the
// name in order to easily detect the end-of-file condition on the input
// file). The student id is read from the given input file.
//
////
void ProcessStudentNameAndId(
ifstream& grades_file, // Input file
char* name // Student name
);
////
//
// Function ProcessStudentScores is the central processing function of the
// entire program. It is called once for each graded item. The grades_file
// input parameter is the file from which scores are read. The graded_counter
// input controls how items are processed. Specifically, if the graded counter
// is non-zero, then a score is read, processed, and output to the terminal.
// If the graded counter is zero then a score is read, discarded, and blanks
// are written to the terminal where the score would go. is performed. If
// processing is performed, the graded counter is decremented by one, for use
// in the next call to this function.
//
// Score processing consists of computing three sums: the weighted score sum
// for the TOTAL column, the running sum for the graded item being processed,
// and the running sum of squares for the item. The percent input parameter is
// used to compute the weighted sum. The three sum values are returned in
// reference parameters.
//
////
void ProcessStudentScores(
ifstream& grades_file, // Input file
int& graded_counter, // Counter for outputting non-empty item scores
int percent, // Percent for this graded item
float& weighted_score_sum, // Sum of weighted (i.e., percentaged) scores
float& sum, // Running sum for this graded item
float& sum_sq // Running sum of squares for this graded item
);
////
//
// Function ProcessTotalScore outputs the given weighted score sum to the
// terminal, if num_graded is non-zero. The weighed sum is added to the given
// sum_total and sum_sq_total reference parameters. These parameters hold the
// running sum and sum of squares for the TOTAL column.
//
////
void ProcessTotalScore(
int num_graded, // Number of graded items
float weighted_score_sum, // Sum of weighted scores
float& sum_total, // Running sum of weighted sums
float& sum_sq_total // Running sum of squares of weighted sums
);
////
//
// Function ProcessStatistics computes and outputs the mean and standard
// deviation statistics to the terminal. The num_items and num_graded input
// parameters are used to control how many statistics columns are computed.
// The num_students, sum, and sum of squares inputs are used in the mean and
// standard deviation formulae. ProcessStatistics uses two subfunctions to
// perform its computation: ComputeAndOutputMeans and ComputeAndOutputStdDevs.
// Further processing details are described in the declarations of these
// functions below.
//
////
void ProcessStatistics(
int num_items, // Number of graded items
int num_graded, // Number of items graded so far
int num_students, // Total number of students
float sums[], // Column sums for each item plus TOTAL
float sum_sqs[] // Sum of squares for each item plus TOTAL
);
////
//
// Function ComputeAndOutputMeans computes all of the means and outputs them to
// the terminal, with the appropriate heading and spacing. This is an
// intermediate managerial function. Parameter usage is as explained in the
// parent function ProcessStatistics. The real work is done in the subfunction
// ComputeAndOuputOneMean, q.v.
//
////
void ComputeAndOutputMeans(
int num_items, // Number of graded items
int num_graded, // Number of items graded so far
int num_students, // Total number of students
float sums[] // Column sums for each item plus TOTAL
);
////
//
// Function ComputeAndOutputStdDevs computes all of the standard deviations and
// outputs them to the terminal, with the appropriate heading and spacing.
// This is an intermediate managerial function. Parameter usage is as
// explained in the parent function ProcessStatistics. The real work is done
// in the subfunction ComputeAndOuputOneStdDev, q.v.
//
////
void ComputeAndOutputStdDevs(
int num_items, // Number of graded items
int num_graded, // Number of items graded so far
int num_students, // Total number of students
float sums[], // Column sums for each item plus TOTAL
float sum_sqs[] // Sum of squares for each item plus TOTAL
);
////
//
// Function ComputeAndOutputOneMean computes and outputs the mean for one
// graded item. The num_students and sum input parameters are used in the
// normal formula for the arithmetic mean, which is:
//
// mean = sum / num_students
//
// The graded_counter reference parameter is used to control processing in
// precisely the same manner as it is used in function ProcessStudentScores,
// q.v.
//
////
void ComputeAndOutputOneMean(
int num_students, // Total number of students
float sum, // Sum for column being computed
int& graded_counter // Counter for outputting non-empty item scores
);
////
//
// Function ComputeAndOutputOneStdDev computes and outputs the standard
// deviation for one graded item. The num_students, sum, and sum_sq input
// parameters are used in a computationally-oriented version of the standard
// deviation formula, which is:
//
// sqrt((sum_sq - pow(sum, 2) / num_students) / (num_students - 1))
//
// This version of the std dev formula is advantageous here because it does not
// involve the mean, and thereby avoids the need to save the mean values and
// send them into this function.
//
// The graded_counter reference parameter is used to control processing in
// precisely the same manner as it is used in function ProcessStudentScores,
// q.v.
//
////
void ComputeAndOutputOneStdDev(
int num_students, // Total number of students
float sum, // Sum for column being computed
float sum_sq, // Sum of squares for column being computed
int& graded_counter // Counter for outputting non-empty item scores
);
////
//
// Function main calls subfunctions OpenGradesFile, ProcessHeaderInfo,
// ProcessStudentInfo, and ProcessStatistics to do their things. Main declares
// variables that are used as input and output parameters among its
// subfunctions.
//
////
int main() {
int num_items; // Total number of graded items
int num_graded; // Number of items graded so far
ifstream grades_file; // Input file
int num_students; // Count of number of students
//
// Set up floating point output format always to print decimal point and
// not to use scientific notation.
//
cout.setf(ios::fixed, ios::floatfield);
cout.setf(ios::showpoint);
//
// Open the grades file.
//
OpenGradesFile(grades_file);
//
// If a grades file was found, proceed with the work, otherwise do
// nothing.
//
if (grades_file) {
//
// Read the first number from the file to determine the size of the
// percent, sum, and sum of squares arrays.
//
grades_file >> num_items;
//
// Declare the three processing arrays. Placing these declarations
// here is an exception to the CSC 101 convention that declarations
// should always come before statements. It is done this way so that
// the size of the arrays can be determined by the value read from the
// grades file.
//
// Note that the size of the sums and sum of squares arrays is
// num_items + 1. This allows the last element of these arrays to be
// used for the TOTAL column.
//
int percents[num_items]; // Percentages for each graded item
float sums[num_items+1]; // Column sums for each item plus TOTAL
float sum_sqs[num_items+1]; // Sum of squares for each item plus TOTAL
//
// Process all remaining header info, up to and including the "======".
//
ProcessHeaderInfo(grades_file, num_items, num_graded, percents);
//
// Process all of the student grade info in the remainder of the file.
//
ProcessStudentInfo(grades_file, num_items, num_graded, num_students,
percents, sums, sum_sqs);
//
// Compute and output the statistics.
//
ProcessStatistics(num_items, num_graded, num_students, sums, sum_sqs);
}
cout << endl;
return 0;
}
void OpenGradesFile(ifstream& grades_file) {
char file_name[50]; // File name input
//
// Prompt for and input the name of the grades file.
//
cout << "Input the name of the grades file: ";
cin >> file_name;
cout << endl;
//
// Open the grades file, issuing an error message if it cannot be opened.
//
grades_file.open(file_name);
if (! grades_file) {
cout << "Given grades file not found or could not be opened.";
}
}
void ProcessHeaderInfo(ifstream& grades_file, int num_items, int& num_graded,
int percents[]) {
int item_counter; // Counter for reading item info.
//
// Input the number of items and number graded and initialize an item
// counter to the number of items.
//
InputAndInitCounters(grades_file, num_items, num_graded, item_counter);
//
// Output the beginning of the column-heading line, formatted per the
// specs.
//
cout << "Name "
<< " Id ";
//
// Process the info for each item.
//
for (item_counter = 0 ; item_counter < num_items ; item_counter++) {
ProcessItemInfo(grades_file, percents[item_counter]);
}
//
// Output the rest of the column heading, which consists of the TOTAL label
// and the line of equals signs.
//
OutputRestOfColumnHeading(grades_file, num_items);
}
void InputAndInitCounters(ifstream& grades_file, int num_items,
int& num_graded, int& item_counter) {
//
// Read in number of items graded so far.
//
grades_file >> num_graded;
//
// Initialize the item counter to be used for reading header info for each
// item.
//
item_counter = num_items;
}
void ProcessItemInfo(ifstream& grades_file, int& percent) {
char label[6]; // Label for each graded item
//
// Read in the next label and percent pair. Output the label immediately,
// and store the percent in the percent reference parameter.
//
grades_file >> label;
cout << " " << setw(5) << label << " ";
grades_file >> percent;
}
void OutputRestOfColumnHeading(ifstream& grades_file, int num_items) {
char ignore[7]; // String to ignore "======" separator
int equal_sign_counter; // Counter for '=' chars in heading separator
//
// Read in and ignore "======" separator line.
//
grades_file >> ignore;
//
// Output the TOTAL column heading, followed by a newline.
//
cout << " TOTAL"
<< endl;
//
// Output the appropriate-length "======= ... " separator line.
//
equal_sign_counter = 25 + 7 + num_items * 7 + 8;
while (equal_sign_counter > 0) {
cout << "=";
equal_sign_counter--;
}
cout << endl;
}
void ProcessStudentInfo(ifstream& grades_file,
int num_items, int num_graded, int& num_students,
int percents[], float sums[], float sum_sqs[]) {
int item_counter; // Counter for reading item info
int graded_counter; // Counter for outputting non-empty item scores
char name[25]; // Student name input
float weighted_score_sum; // Sum of weighted (i.e., percentaged) scores
//
// Initialize the statistics variables.
//
InitStatsVars(num_students, num_items, sums, sum_sqs);
//
// Read first student name.
//
grades_file >> name;
//
// Continue reading and outputting student info until end of file. Each
// loop iteration handles one student.
//
while (grades_file) {
//
// Increment the number of students by 1.
//
num_students++;
//
// Process the already read-in name and the yet to be read-in id.
//
ProcessStudentNameAndId(grades_file, name);
//
// Initialize graded item counter, and weighted sum for this student.
//
graded_counter = num_graded;
weighted_score_sum = 0;
//
// Process the scores for this student, doing all necessary computation
// and output for each set of student data.
//
for (item_counter = 0 ; item_counter < num_items ; item_counter++) {
ProcessStudentScores(grades_file, graded_counter,
percents[item_counter], weighted_score_sum,
sums[item_counter], sum_sqs[item_counter]);
}
//
// Process the total score for this student. Note the index of
// num_items for the two sum arrays. This uses the last element of the
// arrays for the value to be used for the TOTAL column.
//
ProcessTotalScore(num_graded, weighted_score_sum, sums[num_items],
sum_sqs[num_items]);
//
// Input the next student name.
//
grades_file >> name;
}
}
void InitStatsVars(int& num_students, int num_items, float sums[],
float sum_sqs[]) {
int i; // Loop and array index.
//
// Initialize the number of students to 0.
//
num_students = 0;
//
// Initialize column sum and squares sum for each item.
//
for (i = 0; i < num_items + 1; i++) {
sums[i] = 0;
sum_sqs[i] = 0;
}
}
void ProcessStudentNameAndId(ifstream& grades_file, char* name) {
char id[5]; // Student id as a string. Note that it's not
// an int, so we don't ignore leading zeros.
int blank_counter; // Counter for padding name output with blanks
//
// Output the student name, padded on the right by spaces up to
// column 26.
//
cout << name;
blank_counter = 25 - strlen(name);
while (blank_counter > 0) {
cout << ' ';
blank_counter--;
}
//
// Input and output the student id, in a 6-char-wide column.
//
grades_file >> id;
cout << setw(6) << id << " ";
}
void ProcessStudentScores(ifstream& grades_file, int& graded_counter,
int percent, float& weighted_score_sum, float& sum, float& sum_sq) {
int score; // Graded item score input
//
// Input a score.
//
grades_file >> score;
//
// If the graded item counter has not expired, output the score and
// perform the running computations.
//
if (graded_counter > 0) {
//
// Output the score.
//
cout << " " << setw(5) << score << " ";
//
// Add in the weighted score to the weighted sum.
//
weighted_score_sum =
weighted_score_sum + score * 0.01 * percent;
//
// Add in the score for the column sum of this item, as well
// as squares sum.
//
sum = sum + score;
sum_sq = sum_sq + pow(score, 2);
}
//
// If the graded item counter has expired, output seven blanks of space for
// the ungraded item and do no running computation
//
else {
cout << " ";
}
//
// Unconditionally decrement the graded item counter.
//
graded_counter--;
}
void ProcessTotalScore(int num_graded, float weighted_score_sum,
float& sum_total, float& sum_sq_total) {
//
// Output the weighted total for the current student, as long as num_graded
// is non-zero.
//
if (num_graded > 0) {
cout << " " << setw(6) << setprecision(2)
<< weighted_score_sum;
}
//
// Add in the weighted sum for this student to the running weighted sum, as
// well as sum of squares.
//
sum_total = sum_total + weighted_score_sum;
sum_sq_total = sum_sq_total + pow(weighted_score_sum, 2);
//
// Output a newline at end of one student's info.
//
cout << endl;
}
void ProcessStatistics(int num_items, int num_graded, int num_students,
float sums[], float sum_sqs[]) {
//
// Output the stats heading.
//
cout << endl << "STATISTICS:"<< endl;
//
// Compute and output the means stats row.
//
ComputeAndOutputMeans(num_items, num_graded, num_students, sums);
//
// Compute and output the standard deviation stats row.
//
ComputeAndOutputStdDevs(num_items, num_graded, num_students,
sums, sum_sqs);
}
void ComputeAndOutputMeans(int num_items, int num_graded, int num_students,
float sums[]) {
int graded_counter; // Counter for outputting non-empty item scores
int item_counter; // Counter for reading item info
//
// Output the mean row heading.
//
cout << setprecision(2) << " Mean ";
//
// Initialize the graded item counter.
//
graded_counter = num_graded;
//
// Compute and output the mean for each graded column.
//
for (item_counter = 0 ; item_counter < num_items ; item_counter++) {
ComputeAndOutputOneMean(num_students, sums[item_counter],
graded_counter);
}
//
// Output one extra space in front of mean for TOTAL column, and set graded
// counter to num_graded to force output if appropriate. As in
// ProcessStudentInfo, not the use of num_items as the index for the TOTAL
// column array value.
//
cout << " ";
graded_counter = num_graded;
ComputeAndOutputOneMean(num_students, sums[num_items], graded_counter);
//
// Output a newline at the end of the mean output.
//
cout << endl;
}
void ComputeAndOutputStdDevs(int num_items, int num_graded, int num_students,
float sums[], float sum_sqs[]) {
int graded_counter; // Counter for outputting non-empty item scores
int item_counter; // Counter for reading item info
//
// Output the std dev row heading.
//
cout << setprecision(2) << " Standard deviation ";
//
// Initialize the graded item counter.
//
graded_counter = num_graded;
//
// Compute and output the std dev for each graded column.
//
for (item_counter = 0 ; item_counter < num_items ; item_counter++) {
ComputeAndOutputOneStdDev(num_students, sums[item_counter],
sum_sqs[item_counter], graded_counter);
}
//
// Output one extra space in front of std dev for TOTAL column, and set
// graded counter to num_graded to force output if appropriate.
//
cout << " ";
graded_counter = num_graded;
ComputeAndOutputOneStdDev(num_students, sums[num_items],
sum_sqs[num_items], graded_counter);
//
// Output a newline at the end of the std dev output.
//
cout << endl;
}
void ComputeAndOutputOneMean(int num_students, float sum,
int& graded_counter) {
float mean; // Mean for column being computed
//
// If the graded counter has not expired, compute and output the mean.
//
if (graded_counter > 0) {
mean = sum / num_students;
cout << " " << setw(5) << mean << " ";
}
//
// If the graded item counter has expired, output seven blanks of space for
// the ungraded item and do no running computation.
//
else {
cout << " ";
}
//
// Unconditionally decrement the graded item and item counters.
//
graded_counter--;
}
void ComputeAndOutputOneStdDev(int num_students, float sum, float sum_sq,
int& graded_counter) {
//
// If the graded counter has not expired, output the std dev.
//
if (graded_counter > 0) {
cout << " " << setw(5)
<< sqrt((sum_sq - pow(sum, 2) / num_students) /
(num_students - 1))
<< " ";
}
//
// If the graded item counter has expired, output seven blanks of space for
// the ungraded item and do no running computation
//
else {
cout << " ";
}
//
// Unconditionally decrement the graded item and item counters.
//
graded_counter--;
}