You are on page 1of 16

C++ STL Algorithms

Generic algorithms
Apply to a wide range of types
E.g., sorting integers (long) or intervals (long, long)

Dont require inheritance relationships


Types substituted need not have a common base class Need only to be models of the algorithms 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: 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

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
Lets 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:


1.Indicate the sequence over which search will occur 2.Represent a position within the sequence 3.Advance to the next element of the sequence 4.Detect the end of the sequence 5.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 cant be traversed via simple pointers?
CSE 332: C++ STL algorithms

Linear Search with Generic Iterators


Third generalization: separate iterator type parameter The STLs linear search algorithm (Austern pp. 13):
template <class Iterator, class T> Iterator find(Iterator first, Iterator last, const T& value) { while (first != last && *first != value) ++first; return first; }

Our first generic algorithm


Searches any one-dimensional sequence of elements

Notice we did not throw an exception


Not found is a normal result, not an aberration
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 couldnt 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<int>::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, Continued


What very basic concept does the last statement in STL find, (return first;) assume?
Asked another way, what must be able to happen to first when its 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 <class Iterator, class 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 Needs


Category Read Access Write Iteration Comparison Output Input Forward Bidirectional Random Access =*p -> [] *p= ++ -- + - += -= == != < > <= >=

=*p

=*p

=*p

->

->

->

*p=

*p=

*p=

++

++

++

++ --

== !=

== !=

== !=

What STL iterator category does find require?


CSE 332: C++ STL algorithms

Organization of Algorithms within the STL


The <algorithm> header file contains
Non-modifying sequence operations
Do some calculation but dont 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 = cout << << << 7; i << " appears count(v.begin(), v.end(), i) " times in v" << endl;

/* output is 7 appears 0 times in v 2 appears 2 times in v */

i = 2; cout << i << " appears << count(v.begin(), v.end(), i) << " times in v" << endl; return 0;

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 functors 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 is there 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 (ensure behavior matches expectations) #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]); ifstream input_file (input_file_name.c_str()); ofstream output_file (output_file_name.c_str()); input_file >> noskipws; istream_iterator<char> inF (input_file); ostream_iterator<char> otF (output_file); copy (inF, istream_iterator<char>(), otF); cout << << << << "copied input file: " input_file_name << endl " to output file: " output_file_name << endl;

return 0; } /* output: cdgill@hive> ./copytest Makefile Makefile2 copied input file: Makefile to output file: Makefile2 cdgill@hive> diff Makefile Makefile2 cdgill@hive> */

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
#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; }

next_permutation algorithm
Generates a specific kind of reordering, called a permutation Can use to generate all possible orders of a given sequence
/* output is original: asdf sorted: adfs permutations: adsf afds afsd dafs dasf dfas dsfa fads fasd fsad fsda sadf sdfa sfad sfda */

asdf dfsa fdas safd adfs

asfd dsaf fdsa sdaf

CSE 332: C++ STL algorithms

Example of Using Numeric Algorithms


accumulate algorithm
Sums up elements in a range (based on a starting sum value)
#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; }

inner_product algorithm
Computes the inner (also known as dot) product of two vectors: sum of the products of their respective elements

/* output is: v contains 1 2 3 2 the sum of the elements in v is 8 the 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 function objects into them Weve looked at how to use a few function objects Next lecture well look at how function objects 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 it so it works as generically as possible
CSE 332: C++ STL algorithms