Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Chapter 22

The details of the STL algorithms

The utility algorithms

The nonmodifying algorithms: search, numerical processing, comparison, and operational

The modifying algorithms

Sorting algorithms

Set algorithms

A large example: auditing voter registrations

Over view of Algorithms

The “magic” behind the algorithms is that they work on iterator intermediaries instead of on the containers themselves. In that way, they are not tied to specific container implementations. All the STL algorithms are implemented as function templates, where the template type parameters are usually iterator types. The iterators themselves are specified as arguments to the function. Recall from Chapter 11 that templatized functions can usually deduce the template types from the function arguments, so you can generally call the algorithms as if they were normal functions, not templates.

The iterator arguments are usually iterator ranges. As explained in Chapter 21, iterator ranges are halfopen such that they include the first element in the range, but exclude the last. The last iterator is really a “past-the-end” marker.

Some algorithms require additional template type parameters and arguments, which are sometimes function callbacks. These callbacks can be function pointers or function objects. Function objects are discussed in more detail in the next section. First, it’s time to take a detailed look at a few algorithms.

The best way to understand the algorithms is to look at some examples. After you’ve seen how a few of them work, it’s easy to pick up the others. This section describes the find(), find_if(), and accumulate() algorithms in detail. The next section presents the function objects, and the final section discusses each of the classes of algorithms with representative samples.

The find() and find_if() Algorithms

find() looks for a specific element in an iterator range. You can use it on elements in any container type. It returns an iterator referring to the element found, or the end iterator of the range. Note that the range specified in the call to find() need not be the entire range of elements in a container; it could be a subset.

If find() fails to find an element, it returns an iterator equal to the end iterator specified in the function call, not the end iterator of the underlying container.

620

Mastering STL Algorithms and Function Objects

Here is an example of find():

#include <algorithm> #include <vector> #include <iostream> using namespace std;

int main(int argc, char** argv)

{

int num;

vector<int> myVector; while (true) {

cout << “Enter a number to add (0 to stop): “; cin >> num;

if (num == 0) { break;

}

myVector.push_back(num);

}

while (true) {

cout << “Enter a number to lookup (0 to stop): “; cin >> num;

if (num == 0) { break;

}

vector<int>::iterator it = find(myVector.begin(), myVector.end(), num); if (it == myVector.end()) {

cout << “Could not find “ << num << endl; } else {

cout << “Found “ << *it << endl;

}

}

return (0);

}

The call to find() is made with myVector.begin() and myVector.end() as arguments, in order to search all the elements of the vector.

Here is a sample run of the program:

Enter a

number

to

add (0

to

stop): 3

Enter a

number

to

add (0

to

stop): 4

Enter a

number

to

add (0

to

stop): 5

Enter a

number

to

add (0

to

stop): 6

Enter a

number

to

add (0

to

stop): 0

Enter a

number to lookup (0 to stop): 5

Found 5

 

 

 

 

 

Enter a

number to lookup (0 to stop): 8

Could not find

8

 

 

 

Enter a

number to lookup (0 to stop): 4

Found 4

 

 

 

 

 

621

Chapter 22

Enter a number to lookup (0 to stop): 2

Could not find 2

Enter a number to lookup (0 to stop): 0

Some containers, such as map and set, provide their own versions of find() as class methods.

If a container provides a method with the same functionality as a generic algorithm, you should use the method instead, because it’s faster. For example, the generic find() algorithm runs in linear time, even on a map iterator, while the find() method on a map runs in logarithmic time.

find_if() is similar to find(), except that it accepts a predicate function callback instead of a simple element to match. A predicate returns true or false. find_if() calls the predicate on each element in the range until the predicate returns true. find_if() then returns an iterator referring to that element. The following program reads test scores from the user, then checks if any of the scores are “perfect.” A perfect score is a score of 100 or higher. The program is similar to the previous example. Only the differences are highlighted.

#include <algorithm> #include <vector> #include <iostream> using namespace std;

bool perfectScore(int num)

{

return (num >= 100);

}

int main(int argc, char** argv)

{

int num;

vector<int> myVector; while (true) {

cout << “Enter a test score to add (0 to stop): “; cin >> num;

if (num == 0) { break;

}

myVector.push_back(num);

}

vector<int>::iterator it = find_if(myVector.begin(), myVector.end(), perfectScore);

if (it == myVector.end()) {

cout << “No perfect scores\n”; } else {

cout << “Found a \”perfect\” score of “ << *it << endl;

}

return (0);

}

622

Mastering STL Algorithms and Function Objects

This program passed a pointer to the perfectScore() function, which the find_if() algorithm then called on each element until it returned true.

Unfortunately, the STL provides no find_all() or equivalent algorithm that returns all instances matching a predicate. Chapter 23 shows you how to write your own find_all() algorithm.

The accumulate() Algorithms

It’s often useful to calculate the sum, or some other arithmetic quantity, of all the elements in a container. The accumulate() function does just that. In its most basic form, it calculates the sum of the elements in a specified range. For example, the following function calculates the arithmetic mean of a sequence of integers in a vector. The arithmetic mean is simply the sum of all the elements divided by the number of elements.

#include <numeric> #include <vector> using namespace std;

double arithmeticMean(const vector<int>& nums)

{

double sum = accumulate(nums.begin(), nums.end(), 0); return (sum / nums.size());

}

Note that accumulate() is declared in <numeric>, not in <algorithm>. Note also that accumulate() takes as its third parameter an initial value for the sum, which in this case should be 0 (the identity for addition) to start a fresh sum.

The second form of accumulate() allows the caller to specify an operation to perform instead of addition. This operation takes the form of a binary callback. Suppose that you want to calculate the geometric mean, which is the product of all the numbers in the sequence to the power of the inverse of the size. In that case, you would want to use accumulate() to calculate the product instead of the sum. You could write it like this:

#include <numeric> #include <vector> #include <cmath> using namespace std;

int product(int num1, int num2)

{

return (num1 * num2);

}

double geometricMean(const vector<int>& nums)

{

double mult = accumulate(nums.begin(), nums.end(), 1, product); return (pow(mult, 1.0 / nums.size()));

}

623