Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
223,55 KB
Nội dung
Chapter 15: Multiple Inheritance 301 ForwardIterator max_element(ForwardIterator first, ForwardIterator last); ForwardIterator max_element(ForwardIterator first, ForwardIterator last, BinaryPredicate binary_pred); Returns an iterator pointing to the first occurrence of the largest value in the range (there may be multiple occurrences of the largest value). Returns last if the range is empty. The first version performs comparisons with operator< and the value r returned is such that * r < *e is false for every element e in the range. The second version compares using binary_pred and the value r returned is such that binary_pred (*r, *e) is false for every element e in the range. void replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value); void replace_if(ForwardIterator first, ForwardIterator last, Predicate pred, const T& new_value); OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value); OutputIterator replace_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate pred, const T& new_value); Each of the “replace” forms moves through the range [first, last) , finding values that match a criterion and replacing them with new_value . Both replace( ) and replace_copy( ) simply look for old_value to replace, while replace_if( ) and replace_copy_if( ) look for values that satisfy the predicate pred . The “copy” versions of the functions do not modify the original range but instead make a copy with the replacements into result (incrementing result after each assignment). Example To provide easy viewing of the results, this example will manipulate vector s of int . Again, not every possible version of each algorithm will be shown (some that should be obvious have been omitted). //: C05:SearchReplace.cpp // The STL search and replace algorithms #include "PrintSequence.h" #include <vector> #include <algorithm> #include <functional> using namespace std; struct PlusOne { bool operator()(int i, int j) { return j == i + 1; } Chapter 15: Multiple Inheritance 302 }; class MulMoreThan { int value; public: MulMoreThan(int val) : value(val) {} bool operator()(int v, int m) { return v * m > value; } }; int main() { int a[] = { 1, 2, 3, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 11 }; const int asz = sizeof a / sizeof *a; vector<int> v(a, a + asz); print(v, "v", " "); vector<int>::iterator it = find(v.begin(), v.end(), 4); cout << "find: " << *it << endl; it = find_if(v.begin(), v.end(), bind2nd(greater<int>(), 8)); cout << "find_if: " << *it << endl; it = adjacent_find(v.begin(), v.end()); while(it != v.end()) { cout << "adjacent_find: " << *it << ", " << *(it + 1) << endl; it = adjacent_find(it + 2, v.end()); } it = adjacent_find(v.begin(), v.end(), PlusOne()); while(it != v.end()) { cout << "adjacent_find PlusOne: " << *it << ", " << *(it + 1) << endl; it = adjacent_find(it + 1, v.end(), PlusOne()); } int b[] = { 8, 11 }; const int bsz = sizeof b / sizeof *b; print(b, b + bsz, "b", " "); it = find_first_of(v.begin(), v.end(), b, b + bsz); print(it, it + bsz, "find_first_of", " "); Chapter 15: Multiple Inheritance 303 it = find_first_of(v.begin(), v.end(), b, b + bsz, PlusOne()); print(it,it + bsz,"find_first_of PlusOne"," "); it = search(v.begin(), v.end(), b, b + bsz); print(it, it + bsz, "search", " "); int c[] = { 5, 6, 7 }; const int csz = sizeof c / sizeof *c; print(c, c + csz, "c", " "); it = search(v.begin(), v.end(), c, c + csz, PlusOne()); print(it, it + csz,"search PlusOne", " "); int d[] = { 11, 11, 11 }; const int dsz = sizeof d / sizeof *d; print(d, d + dsz, "d", " "); it = find_end(v.begin(), v.end(), d, d + dsz); print(it, v.end(),"find_end", " "); int e[] = { 9, 9 }; print(e, e + 2, "e", " "); it = find_end(v.begin(), v.end(), e, e + 2, PlusOne()); print(it, v.end(),"find_end PlusOne"," "); it = search_n(v.begin(), v.end(), 3, 7); print(it, it + 3, "search_n 3, 7", " "); it = search_n(v.begin(), v.end(), 6, 15, MulMoreThan(100)); print(it, it + 6, "search_n 6, 15, MulMoreThan(100)", " "); cout << "min_element: " << *min_element(v.begin(), v.end()) << endl; cout << "max_element: " << *max_element(v.begin(), v.end()) << endl; vector<int> v2; replace_copy(v.begin(), v.end(), back_inserter(v2), 8, 47); print(v2, "replace_copy 8 -> 47", " "); replace_if(v.begin(), v.end(), bind2nd(greater_equal<int>(), 7), -1); print(v, "replace_if >= 7 -> -1", " "); } ///:~ The example begins with two predicates: PlusOne which is a binary predicate that returns true if the second argument is equivalent to one plus the first argument, and MulMoreThan which returns true if the first argument times the second argument is greater than a value stored in the object. These binary predicates are used as tests in the example. Chapter 15: Multiple Inheritance 304 In main( ) , an array a is created and fed to the constructor for vector<int> v . This vector will be used as the target for the search and replace activities, and you’ll note that there are duplicate elements – these will be discovered by some of the search/replace routines. The first test demonstrates find( ) , discovering the value 4 in v . The return value is the iterator pointing to the first instance of 4, or the end of the input range ( v.end( ) ) if the search value is not found. find_if( ) uses a predicate to determine if it has discovered the correct element. In the above example, this predicate is created on the fly using greater<int> (that is, “see if the first int argument is greater than the second”) and bind2nd( ) to fix the second argument to 8. Thus, it returns true if the value in v is greater than 8. Since there are a number of cases in v where two identical objects appear next to each other, the test of adjacent_find( ) is designed to find them all. It starts looking from the beginning and then drops into a while loop, making sure that the iterator it has not reached the end of the input sequence (which would mean that no more matches can be found). For each match it finds, the loop prints out the matches and then performs the next adjacent_find( ) , this time using it + 2 as the first argument (this way, it moves past the two elements that it already found). You might look at the while loop and think that you can do it a bit more cleverly, to wit: while(it != v.end()) { cout << "adjacent_find: " << *it++ << ", " << *it++ << endl; it = adjacent_find(it, v.end()); } Of course, this is exactly what I tried at first. However, I did not get the output I expected, on any compiler. This is because there is no guarantee about when the increments occur in the above expression. A bit of a disturbing discovery, I know, but the situation is best avoided now that you’re aware of it. The next test uses adjacent_find( ) with the PlusOne predicate, which discovers all the places where the next number in the sequence v changes from the previous by one. The same while approach is used to find all the cases. find_first_of( ) requires a second range of objects for which to hunt; this is provided in the array b . Notice that, because the first range and the second range in find_first_of( ) are controlled by separate template arguments, those ranges can refer to two different types of containers, as seen here. The second form of find_first_of( ) is also tested, using PlusOne . search( ) finds exactly the second range inside the first one, with the elements in the same order. The second form of search( ) uses a predicate, which is typically just something that defines equivalence, but it also opens some interesting possibilities – here, the PlusOne predicate causes the range { 4, 5, 6 } to be found. Chapter 15: Multiple Inheritance 305 The find_end( ) test discovers the last occurrence of the entire sequence { 11, 11, 11 } . To show that it has in fact found the last occurrence, the rest of v starting from it is printed. The first search_n( ) test looks for 3 copies of the value 7, which it finds and prints. When using the second version of search_n( ) , the predicate is ordinarily meant to be used to determine equivalence between two elements, but I’ve taken some liberties and used a function object that multiplies the value in the sequence by (in this case) 15 and checks to see if it’s greater than 100. That is, the search_n( ) test above says “find me 6 consecutive values which, when multiplied by 15, each produce a number greater than 100.” Not exactly what you normally expect to do, but it might give you some ideas the next time you have an odd searching problem. min_element( ) and max_element( ) are straightforward; the only thing that’s a bit odd is that it looks like the function is being dereferenced with a ‘ * ’. Actually, the returned iterator is being dereferenced to produce the value for printing. To test replacements, replace_copy( ) is used first (so it doesn’t modify the original vector) to replace all values of 8 with the value 47. Notice the use of back_inserter( ) with the empty vector v2 . To demonstrate replace_if( ) , a function object is created using the standard template greater_equal along with bind2nd to replace all the values that are greater than or equal to 7 with the value -1. Comparing ranges These algorithms provide ways to compare two ranges. At first glance, the operations they perform seem very close to the search( ) function above. However, search( ) tells you where the second sequence appears within the first, while equal( ) and lexicographical_compare( ) simply tell you whether or not two sequences are exactly identical (using different comparison algorithms). On the other hand, mismatch( ) does tell you where the two sequences go out of sync, but those sequences must be exactly the same length. bool equal(InputIterator first1, InputIterator last1, InputIterator first2); bool equal(InputIterator first1, InputIterator last1, InputIterator first2 BinaryPredicate binary_pred); In both of these functions, the first range is the typical one, [first1, last1) . The second range starts at first2 , but there is no “last2” because its length is determined by the length of the first range. The equal( ) function returns true if both ranges are exactly the same (the same elements in the same order); in the first case, the operator== is used to perform the comparison and in the second case binary_pred is used to decide if two elements are the same. bool lexicographical_compare(InputIterator1 first1, InputIterator1 last1 InputIterator2 first2, InputIterator2 last2); bool lexicographical_compare(InputIterator1 first1, InputIterator1 last1 InputIterator2 first2, InputIterator2 last2, BinaryPredicate binary_pred); Chapter 15: Multiple Inheritance 306 These two functions determine if the first range is “lexicographically less” than the second (they return true if range 1 is less than range 2, and false otherwise. Lexicographical equality, or “dictionary” comparison, means that the comparison is done the same way we establish the order of strings in a dictionary, one element at a time. The first elements determine the result if these elements are different, but if they’re equal the algorithm moves on to the next elements and looks at those, and so on. until it finds a mismatch. At that point it looks at the elements, and if the element from range 1 is less than the element from range two, then lexicographical_compare( ) returns true , otherwise it returns false . If it gets all the way through one range or the other (the ranges may be different lengths for this algorithm) without finding an inequality, then range 1 is not less than range 2 so the function returns false . If the two ranges are different lengths, a missing element in one range acts as one that “precedes” an element that exists in the other range. So {‘a’, ‘b’} lexicographically precedes {‘a’, ‘b’, ‘a’ }. In the first version of the function, operator< is used to perform the comparisons, and in the second version binary_pred is used. pair<InputIterator1, InputIterator2> mismatch(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2); pair<InputIterator1, InputIterator2> mismatch(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, BinaryPredicate binary_pred); As in equal( ) , the length of both ranges is exactly the same, so only the first iterator in the second range is necessary, and the length of the first range is used as the length of the second range. Whereas equal( ) just tells you whether or not the two ranges are the same, mismatch( ) tells you where they begin to differ. To accomplish this, you must be told (1) the element in the first range where the mismatch occurred and (2) the element in the second range where the mismatch occurred. These two iterators are packaged together into a pair object and returned. If no mismatch occurs, the return value is last1 combined with the past- the-end iterator of the second range. As in equal( ) , the first function tests for equality using operator== while the second one uses binary_pred . Example Because the standard C++ string class is built like a container (it has begin( ) and end( ) member functions which produce objects of type string::iterator ), it can be used to conveniently create ranges of characters to test with the STL comparison algorithms. However, you should note that string has a fairly complete set of native operations, so you should look at the string class before using the STL algorithms to perform operations. //: C05:Comparison.cpp // The STL range comparison algorithms #include "PrintSequence.h" #include <vector> #include <algorithm> Chapter 15: Multiple Inheritance 307 #include <functional> #include <string> using namespace std; int main() { // strings provide a convenient way to create // ranges of characters, but you should // normally look for native string operations: string s1("This is a test"); string s2("This is a Test"); cout << "s1: " << s1 << endl << "s2: " << s2 << endl; cout << "compare s1 & s1: " << equal(s1.begin(), s1.end(), s1.begin()) << endl; cout << "compare s1 & s2: " << equal(s1.begin(), s1.end(), s2.begin()) << endl; cout << "lexicographical_compare s1 & s1: " << lexicographical_compare(s1.begin(), s1.end(), s1.begin(), s1.end()) << endl; cout << "lexicographical_compare s1 & s2: " << lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end()) << endl; cout << "lexicographical_compare s2 & s1: " << lexicographical_compare(s2.begin(), s2.end(), s1.begin(), s1.end()) << endl; cout << "lexicographical_compare shortened " "s1 & full-length s2: " << endl; string s3(s1); while(s3.length() != 0) { bool result = lexicographical_compare( s3.begin(), s3.end(), s2.begin(),s2.end()); cout << s3 << endl << s2 << ", result = " << result << endl; if(result == true) break; s3 = s3.substr(0, s3.length() - 1); } pair<string::iterator, string::iterator> p = mismatch(s1.begin(), s1.end(), s2.begin()); print(p.first, s1.end(), "p.first", ""); print(p.second, s2.end(), "p.second",""); } ///:~ Chapter 15: Multiple Inheritance 308 Note that the only difference between s1 and s2 is the capital ‘T’ in s2 ’s “Test.” Comparing s1 and s1 for equality yields true , as expected, while s1 and s2 are not equal because of the capital ‘T’. To understand the output of the lexicographical_compare( ) tests, you must remember two things: first, the comparison is performed character-by-character, and second that capital letters “precede” lowercase letters. In the first test, s1 is compared to s1 . These are exactly equivalent, thus one is not lexicographically less than the other (which is what the comparison is looking for) and thus the result is false . The second test is asking “does s1 precede s2 ?” When the comparison gets to the ‘t’ in “test”, it discovers that the lowercase ‘t’ in s1 is “greater” than the uppercase ‘T’ in s2 , so the answer is again false . However, if we test to see whether s2 precedes s1 , the answer is true . To further examine lexicographical comparison, the next test in the above example compares s1 with s2 again (which returned false before). But this time it repeats the comparison, trimming one character off the end of s1 (which is first copied into s3 ) each time through the loop until the test evaluates to true . What you’ll see is that, as soon as the uppercase ‘T’ is trimmed off of s3 (the copy of s1 ), then the characters, which are exactly equal up to that point, no longer count and the fact that s3 is shorter than s2 is what makes it lexicographically precede s2 . The final test uses mismatch( ) . In order to capture the return value, you must first create the appropriate pair p , constructing the template using the iterator type from the first range and the iterator type from the second range (in this case, both string::iterator s). To print the results, the iterator for the mismatch in the first range is p.first , and for the second range is p.second . In both cases, the range is printed from the mismatch iterator to the end of the range so you can see exactly where the iterator points. Removing elements Because of the genericity of the STL, the concept of removal is a bit constrained. Since elements can only be “removed” via iterators, and iterators can point to arrays, vectors, lists, etc., it is not safe or reasonable to actually try to destroy the elements that are being removed, and to change the size of the input range [first, last) (an array, for example, cannot have its size changed). So instead, what the STL “remove” functions do is rearrange the sequence so that the “removed” elements are at the end of the sequence, and the “un-removed” elements are at the beginning of the sequence (in the same order that they were before, minus the removed elements – that is, this is a stable operation). Then the function will return an iterator to the “new last” element of the sequence, which is the end of the sequence without the removed elements and the beginning of the sequence of the removed elements. In other words, if new_last is the iterator that is returned from the “remove” function, then [first, new_last) is the sequence without any of the removed elements, and [new_last, last) is the sequence of removed elements. If you are simply using your sequence, including the removed elements, with more STL algorithms, you can just use new_last as the new past-the-end iterator. However, if you’re Chapter 15: Multiple Inheritance 309 using a resizable container c (not an array) and you actually want to eliminate the removed elements from the container you can use erase( ) to do so, for example: c.erase(remove(c.begin(), c.end(), value), c.end()); The return value of remove( ) is the new_last iterator, so erase( ) will delete all the removed elements from c . The iterators in [new_last, last) are dereferenceable but the element values are undefined and should not be used. ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value); ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, Predicate pred); OutputIterator remove_copy(InputIterator first, InputIterator last, OutputIterator result, const T& value); OutputIterator remove_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate pred); Each of the “remove” forms moves through the range [first, last) , finding values that match a removal criterion and copying the un-removed elements over the removed elements (thus effectively removing them). The original order of the un-removed elements is maintained. The return value is an iterator pointing past the end of the range that contains none of the removed elements. The values that this iterator points to are unspecified. The “if” versions pass each element to pred( ) to determine whether it should be removed or not (if pred( ) returns true , the element is removed). The “copy” versions do not modify the original sequence, but instead copy the un-removed values into a range beginning at result , and return an iterator indicating the past-the-end value of this new range. ForwardIterator unique(ForwardIterator first, ForwardIterator last); ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate binary_pred); OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result); OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate binary_pred); Each of the “unique” functions moves through the range [first, last) , finding adjacent values that are equivalent (that is, duplicates) and “removing” the duplicate elements by copying over them. The original order of the un-removed elements is maintained. The return value is an iterator pointing past the end of the range that has the adjacent duplicates removed. Because only duplicates that are adjacent are removed, it’s likely that you’ll want to call sort( ) before calling a “unique” algorithm, since that will guarantee that all the duplicates are removed. The versions containing binary_pred call, for each iterator value i in the input range: binary_pred(*i, *(i-1)); Chapter 15: Multiple Inheritance 310 and if the result is true then *(i-1) is considered a duplicate. The “copy” versions do not modify the original sequence, but instead copy the un-removed values into a range beginning at result , and return an iterator indicating the past-the-end value of this new range. Example This example gives a visual demonstration of the way the “remove” and “unique” functions work. //: C05:Removing.cpp // The removing algorithms #include "PrintSequence.h" #include "Generators.h" #include <vector> #include <algorithm> #include <cctype> using namespace std; struct IsUpper { bool operator()(char c) { return isupper(c); } }; int main() { vector<char> v(50); generate(v.begin(), v.end(), CharGen()); print(v, "v", ""); // Create a set of the characters in v: set<char> cs(v.begin(), v.end()); set<char>::iterator it = cs.begin(); vector<char>::iterator cit; // Step through and remove everything: while(it != cs.end()) { cit = remove(v.begin(), v.end(), *it); cout << *it << "[" << *cit << "] "; print(v, "", ""); it++; } generate(v.begin(), v.end(), CharGen()); print(v, "v", ""); cit = remove_if(v.begin(), v.end(), IsUpper()); [...]... #include "NString.h" #include "PrintSequence.h" #include "Generators.h" #include " /require.h" #include #include #include #include #include using namespace std; Chapter 15: Multiple Inheritance 313 // For sorting NStrings and ignore string case: struct NoCase { bool operator()( const NString& x, const NString& y) { /* Somthing's wrong with this approach... C0 5:SpecialList.cpp // Using the second version of transform() #include "Inventory.h" #include "PrintSequence.h" #include #include #include #include using namespace std; struct Discounter { Inventory operator()(const Inventory& inv, float discount) { return Inventory(inv.getItem(), inv.getQuantity(), inv.getValue() * (1 - discount)); } }; struct DiscGen { DiscGen()... //: C0 5:SortedSearchTest.cpp //{L} /C0 4/StreamTokenizer // Test searching in sorted ranges #include " /C0 4/StreamTokenizer.h" #include "PrintSequence.h" #include "NString.h" #include " /require.h" #include #include #include #include using namespace std; int main() { ifstream in( "SortedSearchTest.cpp"); assure (in, "SortedSearchTest.cpp"); StreamTokenizer words (in) ;... with for_each( ) that has data members to hold the totals: //: C0 5:CalcInventory.cpp // More use of for_each() #include "Inventory.h" #include "PrintSequence.h" #include #include using namespace std; // To calculate inventory totals: class InvAccum { int quantity; int value; public: InvAccum() : quantity(0), value(0) {} void operator()(const Inventory& inv) { quantity += inv.getQuantity();... keeps a char* identifier to make tracking the output easier The CountedVector is inherited from vector, and in the constructor it creates some Counted objects, handing each one your desired char* The CountedVector makes testing quite simple, as you’ll see //: C0 5:ForEach.cpp // Use of STL for_each() algorithm #include "Counted.h" #include #include #include using... } }; Chapter 15: Multiple Inheritance 330 int main() { vector vi; generate_n(back_inserter(vi), 15, InvenGen()); print(vi, "vi"); vector disc; generate_n(back_inserter(disc), 15, DiscGen()); print(disc, "Discounts:"); vector discounted; transform(vi.begin(),vi.end(), disc.begin(), back_inserter(discounted), Discounter()); print(discounted, "discounted"); } ///:~ Discounter... count . //: C0 5:SearchReplace.cpp // The STL search and replace algorithms #include "PrintSequence.h" #include <vector> #include <algorithm> #include <functional> using. /require.h" #include <algorithm> #include <fstream> #include <queue> #include <vector> #include <cctype> using namespace std; Chapter 15: Multiple Inheritance . int csz = sizeof c / sizeof *c; print (c, c + csz, " ;c& quot;, " "); it = search(v.begin(), v.end(), c, c + csz, PlusOne()); print(it, it + csz,"search PlusOne", "