A studentβs grade in a class is based on 10 quiz scores (each quiz score is a decimal number). Their score is computed by averaging all but the lowest score. We would like to write a program that reads in a list of values and calculates an average for a student.
The first part seems easy enough. It will be hard to test as it involves user input. But the work itself seems easy enough. We could put that work in a function that reads in the scores and returns a vector of doubles. vector<double> getData()
Calculating the average involves a few pieces. We need to figure out the average, but we also need to remove the lowest score before doing so. We could do that in a few ways:
We could find and remove the lowest value from the list.
We donβt really need a whole new list. And we do not want to permanently remove the lowest score. So the last option seems best. We can find the lowest value and subtract it from the total before dividing by 9.
Finding the lowest value seems like a good self-contained job for a function to do. We need something that takes in the vector and returns the one lowest value: `double minValue(const vector<double>& scores)`
Note that the function does not assume the data is any particular length. But it does assume there is at least one value. Because there is no logical answer for βwhat is the minimum value in an empty vector?β we will throw an exception if that assumption is violated.
When writing a function to find a particular value in a vector (like the minimum or maximum), it is more useful in general to return the index of the element rather than the value of the element. If I tell you that the smallest element is at index 3, you can use .at(3) to find the value. If I tell you that the smallest value is 68.4, there is no easy way to find the index.
We can now worry about finding the average of all but the lowest items. If there are x items, we can add them all up, subtract the smallest value (which we have a function to find), and divide by x - 1. Since we already have a sum function (see SubsectionΒ 13.8.2) let us use it to help do the job.
Given our two helper function, finding the average sounds pretty easy. It should just be a few lines of code. But it is worth turning it into a function like double avgWithoutLow(const vector<double>& scores). That will allow us to run some automated tests on it.
How letβs loop back to worry about getting our input. Testing user input isnβt easy to do with the tools we have. But by testing the rest of the logic without relying on user input, we should only have to worry about whether the input code is working or not. We already have confidence in the rest of the logic.
Finish the getData function. It already reads in 10 doubles. You need to make a vector to store them in, add each value to the vector, and then return the vector when you are done.
The easiest approach is to start with an empty vector and use push_back to add each value. But you also can start with a vector of 10 doubles and then use the loop counter to place each value in the correct location of the vector.
At this point it would be easy to call the avgWithoutLow function on the scores vector to make the fully functioning program. All of the various tasks have been built and tested. You are encouraged to combine the functions into a single program and test it out on your own.