cse 332: c++ stl algorithms c++ stl algorithms generic algorithms –apply to a wide range of types...
TRANSCRIPT
CSE 332: C++ STL algorithms
C++ STL Algorithms• Generic algorithms
– Apply to a wide range of types• E.g., sorting integers (int) vs. intervals (pair<int, int>)
– Don’t require inheritance relationships• Types substituted need not have a common base class• Need only to be models of the algorithm’s concept
• Implementations in C++– Rely on templates, interface-based polymorphism– Algorithms are implemented as function templates– Use types that model iterator concepts– Iterators in turn give access to containers
CSE 332: C++ STL algorithms
Example Revisited: Linear Search
• From Austern: “Generic Programming and the STL”• Sequential (linear) search: find char c in string s
char * strchr (char* s, char c){ while (*s != 0 && *s != c){
++s; } return *s == c ? s : (char *) 0; }
• Problem: not very general– “Range” of iteration is always defined up to ‘\0’ character– Only works for a “zero terminated” string in C/C++
CSE 332: C++ STL algorithms
Review in Detail: Linear Search with Ranges
• First generalization (Austern, pp. 11): use a range
char * find1 (char* first, char* last, char c){ while (first != last && *first != c) ++first; return first;}
• Gives an explicit range (calculate its length – how?)• Assumes first is before last (can check – how?)• Note how caller checks for success changed: why?
CSE 332: C++ STL algorithms
General Requirements for Linear Search
• Before we try to improve the algorithm further– Let’s come up with a definition of what it needs to do– This helps to plan what to require and what to leave flexible
• Any linear search implementation must offer a way to:– Indicate the sequence over which search will occur– Represent a position within the sequence– Advance to the next element of the sequence– Detect the end of the sequence– Return a value as an indication of success or failure
• Goal: meet these requirements flexibly and efficiently
CSE 332: C++ STL algorithms
Linear Search over Parameterized Types
• Second generalization: use templates to parameterize the function argument types
template <typename T> T * find2(T * first, T * last, const T & value){ while (first != last && *first != value)
++first; return first;}
• How much did the find1 code need to change?• One last problem
– What if we want to apply this to a data structure whose ranges can’t be traversed via simple pointers?
CSE 332: C++ STL algorithms
Linear Search with Generic Iterators
• Third generalization: separate iterator type parameter• The STL’s linear search algorithm (Austern pp. 13):
template <typename Iterator, typename T>Iterator find (Iterator first, Iterator last,
const T & value) { while (first != last && *first != value) ++first; return first;}
• Notice how algorithm depends on the iterators• Notice how refinements made algorithm more abstract
– … but still essentially does the same thing– i.e., algorithm structure (and time complexity) is the same
CSE 332: C++ STL algorithms
Algorithm Concepts and Models
• Remember a concept gives a set of type requirements– Classify/categorize types (e.g., random access iterators)– Tells whether or not a type can or cannot be used with a
particular STL algorithm (get a compiler error if it cannot)• E.g., we couldn’t use a linked list iterator in find1 or even find2
• Any specific type that meets the requirements is a model of that concept– E.g., list<char>::iterator vs. char * in find
• Different abstractions (bi-linked list vs. array iterators)• No inheritance-based relationship between them • But both model iterator concept necessary for find
CSE 332: C++ STL algorithms
Concepts and Modeling Review, Continued• What very basic concept does the last statement in
STL find, (the line return first;) assume?– Asked another way, what must be able to happen to first
when it’s returned from function find? – Same requirement imposed by by-value iterator parameters
• What other capabilities are required of the Iterator and T type parameters by the STL find algorithm ?
template <typename Iterator, typename T>Iterator find (Iterator first, Iterator last,
const T & value) { while (first != last && *first != value) ++first; return first;}
CSE 332: C++ STL algorithms
Concepts and Modeling Review, Continued• What very basic concept does the last statement in
STL find, (the line return first;) assume?– Asked another way, what must be able to happen to first
when it’s returned from function find? – Same requirement imposed by by-value iterator parameters
• What other capabilities are required of the Iterator and T type parameters by the STL find algorithm ?
template <typename Iterator, typename T>Iterator find (Iterator first, Iterator last,
const T & value) { while (first != last && *first != value) ++first; return first;}
CSE 332: C++ STL algorithms
Matching an Algorithm to the Iterators it NeedsCategory/ Operation
Output Input Forward BidirectionalRandom
Access
Read =*p(r-value)
=*p(r-value)
=*p(r-value)
=*p(r-value)
Access -> -> ->->
[]
Write *p=(l-value)
*p=(l-value)
*p=(l-value)
*p=(l-value)
Iteration ++ ++ ++ ++ --++ -- + - += -=
Comparison == != == != == !=== != < > <= >=
What STL iterator category does find require?
CSE 332: C++ STL algorithms
What if an Algorithm Has Alternative Versions?
• First approach: dynamic dispatch– Different names of
implementations– Run-time iterator
type test– Calls the best
implementation– What are the
limitations here?
// Based on Austern, pp. 38
template <class Iter, class Distance>
void move_fwd (Iter &i, Distance d) {
while (d>0) {--d; ++i} // O(d)
}
template <class Iter, class Distance>
void move_rand (Iter &i, Distance d) {
i+=d; // O(1)
}
template <class Iter, class Distance>
void move (Iter &i, Distance d) {
if (is_rand(i)) move_rand (i, d)
else if (is_fwd(i)) move_fwd (i, d)
// else ...
}
CSE 332: C++ STL algorithms
Iterator Traits and Category Type Tags• Need a few
concrete types to use as tags– E.g., empty structs– E.g., input, output, fwd, bidir, and rand
• Tags provide yet another associated type for iterators– Iterator category– Again, made
available by using the traits idiom
struct input {}; // empty structs for type tags
struct output {};
struct fwd : public input {}; // note inheritance
struct bidir : public fwd {};
struct rand : public bidir {};
template <typename I> struct iterator_traits {
...
typedef typename I::iterator_category
iterator_category;
};
template <typename T> struct iterator_traits<T*> {
...
typedef rand iterator_category;
};
template <typename T>
struct iterator_traits<const T*> {
...
typedef rand iterator_category;
};
(actually, random_access_iterator_tag)
CSE 332: C++ STL algorithms
Algorithm Dispatching via Category Tags• Static dispatching
– Implementations provide different signatures
– Iterator type is evaluated at compile-time
– Links to the best implementation
• Notice how type tags are used
// Based on Austern, pp. 38, 39
template <class Iter, class Distance>
void move (Iter i, Distance d, fwd) {
while (d>0) {--d; ++i;} // O(d)
}
template <class Iter, class Distance>
void move (Iter i, Distance d, rand) {
i += d; // O(1)
}
template <class Iter, class Distance>
void move (Iter i, Distance d) {
move (i, d,
iterator_traits<Iter>::
iterator_category()
)
}
concrete tag (empty struct) type
explicit constructor call
concrete tag (empty struct) type
CSE 332: C++ STL algorithms
Organization of Algorithms within the STL• The <algorithm> header file contains
– Non-modifying sequence operations• Do some calculation but don’t change sequence itself• Examples include count, count_if
– Mutating sequence operations• Modify the order or values of the sequence elements• Examples include copy, random_shuffle
– Sorting and related operations• Modify the order in which elements appear in a sequence• Examples include sort, next_permutation
• The <numeric> header file contains– General numeric operations
• Scalar and matrix algebra, especially used with vector<T>• Examples include accumulate, inner_product
CSE 332: C++ STL algorithms
Example of Using Non-Modifying Algorithms • count algorithm
– Moves through iterator range
– Checks each position for equality
– Increases count if equal
#include <iostream>#include <vector>#include <algorithm>
using namespace std;
int main (int, char * []) {
vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(2);
int i = 7;
cout << i << " appears " << count(v.begin(), v.end(), i) << " times in v" << endl;
i = 2;
cout << i << " appears " << count(v.begin(), v.end(), i) << " times in v" << endl;
return 0;}
/* output is7 appears 0 times in v2 appears 2 times in v */
CSE 332: C++ STL algorithms
Using a Function Object to Extend an Algorithm • count_if algorithm
– Generalizes the count algorithm
– Instead of comparing for equality to a value
– Applies a given predicate function object (functor)
– If functor’s result is true, increases count
#include <iostream>#include <vector>#include <algorithm>using namespace std;
template <typename T>struct odd { bool operator() (T t) const { return (t % 2) != 0; }};
int main (int, char * []) {
vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(2);
cout << "there are " << count_if(v.begin(), v.end(), odd<int>()) << " odd numbers in v" << endl;
return 0;}
/* output isthere are 2 odd numbers in v */
CSE 332: C++ STL algorithms
Example of Using Mutating Algorithms• copy algorithm
– Copies from an input iterator range into an output iterator
– Note use of default constructor to get an “off-the-end” (here, “end-of-file”) input iterator
– Note use of noskipws (need to make sure container behavior matches what you want to do)
ifstream input_file (input_file_name.c_str());
ofstream output_file (output_file_name.c_str());
input_file >> noskipws; istream_iterator<char> i (input_file); ostream_iterator<char> o (output_file);
copy (i, istream_iterator<char>(), o);
cout << "copied input file: " << input_file_name << endl << " to output file: " << output_file_name << endl;
return 0;}
/* output:cdgill@hive> ./copytest Makefile Makefile2copied input file: Makefile to output file: Makefile2cdgill@hive> diff Makefile Makefile2cdgill@hive> */
#include <iostream>
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
using namespace std;
int main (int argc, char * argv[]) {
if (argc != 3) {return 1;}
string input_file_name (argv[1]);
string output_file_name (argv[2]);
CSE 332: C++ STL algorithms
Example of Using Sorting Algorithms• sort algorithm
– Reorders a given range– Can also plug in a functor to
change the ordering function• next_permutation
algorithm– Generates a specific kind of
reordering, called a “permutation”
– Can use to generate all possible orders of a given sequence
#include <iostream>#include <string>#include <algorithm>
using namespace std;
int main (int, char * []) {
string s = "asdf"; cout << "original: " << s << endl;
sort (s.begin(), s.end()); cout << "sorted: " << s << endl;
string t (s); cout << "permutations:" << endl;
do { next_permutation (s.begin(), s.end()); cout << s << " "; } while (s != t);
cout << endl;
return 0;}
/* output isoriginal: asdfsorted: adfspermutations:adsf afds afsd asdf asfddafs dasf dfas dfsa dsaf dsfa fads fasd fdas fdsa fsad fsda sadf safd sdaf sdfa sfad sfda adfs */
CSE 332: C++ STL algorithms
Example of Using Numeric Algorithms• accumulate
algorithm– Sums up elements in
a range (based on a starting sum value)
• inner_product algorithm– Computes the inner
(also known as “dot”) product of two matrixes: sum of the products of their respective elements
#include <iostream>#include <vector>#include <numeric>
using namespace std;
int main (int, char * []) {
vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(2);
cout << "v contains "; for (size_t s = 0; s < v.size(); ++s) { cout << v[s] << " "; } cout << endl; cout << "the sum of the elements in v is " << accumulate (v.begin(), v.end(), 0) << endl; cout << "the inner product of v and itself is " << inner_product (v.begin(), v.end(), v.begin(), 0) << endl;
return 0;}
/* output is:v contains 1 2 3 2 the sum of the elements in v is 8the inner product of v and itself is 18 */
CSE 332: C++ STL algorithms
Concluding Remarks• STL algorithms give you useful, generic functions
– Combine easily with a variety of containers/iterators– Support many common data structure manipulations
• Finding and modifying values, re-ordering, numeric operations– Reusing them saves you from writing code
• Many STL algorithms can be extended– Especially by plugging functors into them– We’ve looked at how to use a few functors– Next lecture we’ll look at how functors work
• You can also create your own generic algorithms– If something you need is not in the STL– Think about the iterator and data type concept it requires– Implement a version that works as generically as possible– Use traits-based dispatching to support more specific (and
thus more efficient) versions