Solution to Programming Assignment 4, Using Arrays

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--;
}


index | lectures | labs | handouts | assignments | solutions | grades | help