There are many facets to learning โhow to programโ. You need to learn the syntax of one or more languages; you need to develop a mental model for how algorithms operate; you must learn how computers represent data; and you must develop your ability to solve problems using all of these skills and knowledge. When you start learning to program, the low-level details like syntax occupy much of your attention. As you progress, you spend less and less time learning new syntax and more time learning to solve problems.
Problem solving is almost by definition a creative process. If we know how to solve the problem already, we would likely not consider it to be a โproblemโ. Instead it would be a task to get done. Every time you go to solve a problem, it will likely involve doing something new. But few problems are truly unique. For any new problem you are solving, chances are there is a similar problem which already has a solution that you can adapt to meet the new challenge. So an important part of learning to program effectively is developing a โplaybookโ - a set of common problems and algorithms that can solve them.
In this section we will look at some common problems that can be solved using vectors. We will look at the algorithms used to solve them and how to implement them in C++.
We start by assuming the value we are looking for is not present. This is done on line 10 where we set the location to the size of the array. If the size is 4, the only valid values are 0-3, so we know size() represents an impossible value. Then, we traverse the vector to check each element. If we find the target value in the vector, we change the location and break out of the loop. When the loop is done, if location still matches the size(), we know the value was not found.
Another common traversal is a reduce operation, which โreducesโ an vector of values down to a single value. Examples include the sum or product of the elements, the minimum, and the maximum. The following function takes an vector and returns the sum of its elements. A reduction always needs an accumulator variable where we store the answer. As we visit each element, we update that variable as appropriate.
We have already seen one reduction recipe - calculating the sum of the values in a vector. In this recipe, our accumulator is the total and starts with a value of 0:
double sum(const vector<double>& values) {
double total = 0.0;
for (double value : values) {
total += value;
}
return total;
}
Finding the smallest value in a vector can also be thought of as a reduction. In this case, our accumulator needs to keep track of the โsmallest value we have seen so farโ (called minimum in the sample below). As we visit each element, we compare it to minimum, and if it is lower, we need to change minimum to store the new lowest value that we have just discovered. But for this to work, we canโt start with 0 in our accumulator. If we did, we could go through the entire vector and never find anything smaller, and thus end up thinking that 0 was the minimum value. For situations like this it is common to use the first value to initialize the accumulator and then loop through all the elements after the first:
double minValue(const vector<double>& values) {
// assume the first element is the minimum
int minimum = values.at(0);
// loop through the rest of the vector to see if any elements are smaller
for (size_t i = 1; i < values.size(); ++i) {
int value = values.at(i);
if (value < minimum) {
minimum = value; // found a new minimum
}
}
return minimum;
}
A map operation transforms each element in a vector to a new value. In Sectionย 13.7 we saw two versions of the doubleValues function that performed a map operation. The first version created a new vector and returned it. The second version modified the original vector in place.
Finally, a filter operation traverses a vector and only keeps values that meet a certain condition. Although we could do this by removing unwanted values from the vector we start with, it is much easier to build up a vector with just the ones we want. This final sample demonstrates a function that filters out 0 values from a vector, producing a new one with only non-zero values:
Simple does not always mean โbadโ or โinefficientโ. It will almost certainly be faster to build a new vector with just the desired values instead of removing multiple items from the existing vector.
Suppose vector<string> album has already been defined and is storing some number of strings representing song titles. Construct a block of code that counts how many songs have an e in them.
Build a function onlyPassing that takes a vector<char> that contains a list of characters representing letter grades (A, B, C, D, F) and returns a new vector that has all the grades but the Fs.