Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

The next two template functions automate the process of testing the various function object templates. There are two since the function objects are either unary or binary. In testUnary( ), you pass a source and destination vector, and a unary function object to apply to the source vector to produce the destination vector. In testBinary( ), there are two source vectors which are fed to a binary function to produce the destination vector. In both cases, the template functions simply turn around and call the transform( ) algorithm, although the tests could certainly be more complex.

For each test, you want to see a string describing what the test is, followed by the results of the test. To automate this, the preprocessor comes in handy; the T( ) and B( ) macros each take the expression you want to execute. They call that expression, then call print( ), passing it the result vector (they assume the expression changes a vector named r and br, respectively), and to produce the message the expression is “string-ized” using the preprocessor. So that way you see the code of the expression that is executed followed by the result vector.

The last little tool is a generator object that creates random bool values. To do this, it gets a random number from rand( ) and tests to see if it’s greater than RAND_MAX/2. If the random numbers are evenly distributed, this should happen half the time.

In main( ), three vector<int> are created: x and y for source values, and r for results. To initialize x and y with random values no greater than 50, a generator of type URandGen is used; this will be defined shortly. Since there is one operation where elements of x are divided by elements of y, we must ensure that there are no zero values of y. This is accomplished using the transform( ) algorithm, taking the source values from y and putting the results back into y. The function object for this is created with the expression:

bind2nd(plus<int>(), 1)

This uses the plus function object that adds two objects together. It is thus a binary function which requires two arguments; we only want to pass it one argument (the element from y) and have the other argument be the value 1. A “binder” does the trick (we will look at these next). The binder in this case says “make a new function object which is the plus function object with the second argument fixed at 1.”

Another of the tests in the program compares the elements in the two vectors for equality, so it is interesting to guarantee that at least one pair of elements is equivalent; in this case element zero is chosen.

Once the two vectors are printed, T( ) is used to test each of the function objects that produces a numerical value, and then B( ) is used to test each function object that produces a Boolean result. The result is placed into a vector<bool>, and when this vector is printed it produces a ‘1’ for a true value and a ‘0’ for a false value.

Binders

It’s common to want to take a binary function object and to “bind” one of its arguments to a constant value. After binding, you get a unary function object.

Chapter 15: Multiple Inheritance

269

For example, suppose you want to find integers that are less than a particular value, say 20. Sensibly enough, the STL algorithms have a function called find_if( ) that will search through a sequence; however, find_if( ) requires a unary predicate to tell it if this is what you’re looking for. This unary predicate can of course be some function object that you have written by hand, but it can also be created using the built-in function object templates. In this case, the less template will work, but that produces a binary predicate, so we need some way of forming a unary predicate. The binder templates (which work with any binary function object, not just binary predicates) give you two choices:

bind1st(const BinaryFunction& op, const T& t); bind2nd(const BinaryFunction& op, const T& t);

Both bind t to one of the arguments of op, but bind1st( ) binds t to the first argument, and bind2nd( ) binds t to the second argument. With less, the function object that provides the solution to our exercise is:

bind2nd(less<int>(), 20);

This produces a new function object that returns true if its argument is less than 20. Here it is, used with find_if( ):

//: C05:Binder1.cpp

// Using STL "binders" #include "Generators.h" #include "copy_if.h" #include <algorithm> #include <vector> #include <iostream> #include <functional> using namespace std;

int main() {

const int sz = 10; const int max = 40; vector<int> a(sz), r; URandGen urg(max);

ostream_iterator<int> out(cout, " "); generate_n(a.begin(), sz, urg); copy(a.begin(), a.end(), out);

int* d = find_if(a.begin(), a.end(), bind2nd(less<int>(), 20));

cout << "\n *d = " << *d << endl;

//copy_if() is not in the Standard C++ library

//but is defined later in the chapter:

copy_if(a.begin(), a.end(), back_inserter(r), bind2nd(less<int>(), 20));

Chapter 15: Multiple Inheritance

270

copy(r.begin(), r.end(), out); cout << endl;

} ///:~

The vector<int> a is filled with random numbers between 0 and max. find_if( ) finds the first element in a that satisfies the predicate (that is, which is less than 20) and returns an iterator to it (here, the type of the iterator is actually just int* although I could have been more precise and said vector<int>::iterator instead).

A more interesting algorithm to use is copy_if( ), which isn’t part of the STL but is defined at the end of this chapter. This algorithm only copies an element from the source to the destination if that element satisfies a predicate. So the resulting vector will only contain elements that are less than 20.

Here’s a second example, using a vector<string> and replacing strings that satisfy particular conditions:

//: C05:Binder2.cpp // More binders #include <algorithm> #include <vector> #include <string> #include <iostream> #include <functional> using namespace std;

int main() {

ostream_iterator<string> out(cout, " "); vector<string> v, r;

v.push_back("Hi"); v.push_back("Hi"); v.push_back("Hey"); v.push_back("Hee"); v.push_back("Hi"); copy(v.begin(), v.end(), out); cout << endl;

//Replace each "Hi" with "Ho": replace_copy_if(v.begin(), v.end(),

back_inserter(r), bind2nd(equal_to<string>(), "Hi"), "Ho");

copy(r.begin(), r.end(), out); cout << endl;

//Replace anything that's not "Hi" with "Ho": replace_if(v.begin(), v.end(),

not1(bind2nd(equal_to<string>(),"Hi")),"Ho");

Chapter 15: Multiple Inheritance

271

copy(v.begin(), v.end(), out); cout << endl;

} ///:~

This uses another pair of STL algorithms. The first, replace_copy_if( ), copies each element from a source range to a destination range, performing replacements on those that satisfy a particular unary predicate. The second, replace_if( ), doesn’t do any copying but instead performs the replacements directly into the original range.

A binder doesn’t have to produce a unary predicate; it can also create a unary function (that is, a function that returns something other than bool). For example, suppose you’d like to multiply every element in a vector by 10. Using a binder with the transform( ) algorithm does the trick:

//: C05:Binder3.cpp

// Binders aren't limited to producing predicates #include "Generators.h"

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

int main() {

ostream_iterator<int> out(cout, " "); vector<int> v(15);

generate(v.begin(), v.end(), URandGen(20)); copy(v.begin(), v.end(), out);

cout << endl;

transform(v.begin(), v.end(), v.begin(), bind2nd(multiplies<int>(), 10));

copy(v.begin(), v.end(), out); cout << endl;

} ///:~

Since the third argument to transform( ) is the same as the first, the resulting elements are copied back into the source vector. The function object created by bind2nd( ) in this case produces an int result.

The “bound” argument to a binder cannot be a function object, but it does not have to be a compile-time constant. For example:

//: C05:Binder4.cpp

//The bound argument does not have

//to be a compile-time constant #include "copy_if.h"

Chapter 15: Multiple Inheritance

272

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu