0% found this document useful (0 votes)
167 views

CPP04-STLAlgorithms 4up PDF

This document discusses STL algorithms and functional programming concepts in C++. It introduces common STL algorithms like accumulate, transform, copy, and for_each that can be used to map, filter and reduce collections of data. These algorithms abstract away low-level procedural code and allow working with data in a more declarative, functional style. While C++ supports both object-oriented and functional paradigms, combining the two is challenging. STL algorithms are optimized tools that help enable functional programming in C++.

Uploaded by

Satyendra Maurya
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
167 views

CPP04-STLAlgorithms 4up PDF

This document discusses STL algorithms and functional programming concepts in C++. It introduces common STL algorithms like accumulate, transform, copy, and for_each that can be used to map, filter and reduce collections of data. These algorithms abstract away low-level procedural code and allow working with data in a more declarative, functional style. While C++ supports both object-oriented and functional paradigms, combining the two is challenging. STL algorithms are optimized tools that help enable functional programming in C++.

Uploaded by

Satyendra Maurya
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Porter Scobey

http://cs.stmarys.ca/~porter/csc/ref/stl/index_algorithms.html Contents
Stanford 106L, Standard C++ Programming Laboratory
http://web.stanford.edu/class/cs106l/
1. Why STL algorithm? 11. Mapping algorithms
topcoder’s tutorial, Power up C++ with the STL, part I and II
https://www.topcoder.com/community/data-science/data-science-tutorials/ 2. Accumulate 12. Palindrome
power-up-c-with-the-standard-template-library-part-1/
3. STL algorithm naming 13. Utilities - ctype and math
STL Algorithms 4. Iterator categories 14. Magic square
5. Reordering algorithms 15. Substitution cipher
<algorithm>, <numeric>, <iterator>, <functional>
6. Searching algorithms 16. Graph connectivity –
<cctype>, <cmath>
7. Iterator adaptors DFS and BFS
C++, a multi-paradigm programming language,
8. Removal algorithms 17. Dijkstra’s Shortest Path
besides being procedural and object-oriented,
is very much functional with STL 9. Functional Thinking
Pei-yih Ting
10. Optimized machinery: Map / Filter / Reduce
NTOU CS
1 2

Abstract away some chores data.txt


Functional Language
 Commonly seen procedural piece of codes 100  Mathematically a functional is a function of a function, or higher
#include <iostream> 95 order function, ex. Integration, Derivative, arc length, …
low-level mechanical steps
#include <fstream> 92
#include <set>
 Functional programming is a declarative programming paradigm
High level abstract thoughts 89
using namespace std; which models computations as the evaluation of mathematical
100
int main() {
... functions and avoid changing state / mutable data, i.e.
ifstream input("data.txt");  Read the contents of the file programming is done with expressions or declarations instead of
multiset<int> values;
Average = … statements. The output value of a function depends only on the
copy(istream_iterator<int>(input),
int currValue; istream_iterator<int>(), arguments that are input to the function without side effects such
abstraction
Functional

while (input >> currValue) values.begin()));


inserter(values, <algorithm>
values.insert(currValue); that it is easier to understand and predict the behavior of a program.
map
 Add the values together
double total = 0.;  Functional and Object-oriented styles are not easy to combine.
for (multiset<int>::iterator
double itr = values.begin(); itr !=values.end(),
total = accumulate(values.begin(), values.end(); ++itr)
0.0);  Bjarne Stroustrup's: C++ was designed to allow programmers to
total += *itr; reduce switch between paradigms as needed. The language is not designed
<numeric>
cout << "Average = " << total / values.size() << endl; to make it easy for combining different paradigms. Most of
return 0; Stroustrup’s examples regarding OOP touch the STL very little. He
}  Calculate the average
3 creates very distinct layers. 4
Tools for Functional Abstraction accumulate()
Your first high-level machinery
 Algorithms: optimized machinery  #include <numeric>

 Map:  accumulate sums up the elements in a range and returns the result
initial value
transform / copy / for_each / replace / sort / partition multiset<int> values; [begin(), end())
 Filter: …
removal (find and erase)
double total = accumulate(values.begin(), values.end(), 0.0);
 Reduce: accumulate(values.lower_bound(42), values.upperbound(99), 0.0);
accumulate / min_element / count / equal / search / selection Your first higher order function (user customized)
 accumulate is a general-purpose function for transforming a
customized with callable objects collection of elements into a single value (in functional language
(functions and functors) terms: reduce / collect / convert / join / fold)
int findLess(int smallestSoFar, int current) {
 Core data structure: container return current < smallestSoFar ? current : smallestSoFar; }
int smallest = accumulate(values.begin(), values.end(),
5 <limits> numeric_limits<int>::max(), findLess); 6

Advantages Algorithm Naming Conventions


 More than 50 STL algorithms (<algorithm> and <numeric>)
 Simplicity:  xxx_if (replace_if, count_if, …): means the algorithm will perform a
 Leverage off of code that is already written for you rather than task on elements only if they meet a certain criterion
reinventing the code from scratch; don’t duplicate code require a predicate function: accepts an element and returns a bool
 Correctness: e.g. bool isEven(int value) { return value %2 == 0; }
cout << count_if(myVec.begin(), myVec.end(), isEven);
 already tested without manual mistakes
 xxx_copy (remove_copy, partial_sort_copy, …): performs some task
 Speed: on a range of data and store the result in another location (immutable)
 STL algorithms are optimized such that they are faster than most e.g. int iarray[] = {0, 1, 2, 3, 3, 4, 3}; vector<int> myV(7);
code you could write by hand reverse_copy(iarray, iarray+7, myV.begin());
 Clarity:  xxx_n (generate_n, search_n, …): performs a certain operation n
 With a customized for loop, you would have to read each line in the times (on n elements in the container)
e.g. fill_n(myDeque.begin(), 10, 0); Two consecutive 3
loop before you understood what the code did.
7 vector<int>::iterator it=search_n(myV.begin(), myV.end(), 2, 3); 8
Iterator Categories Iterator Categories (cont’d)
 STL iterators are categorized based on their relative power
 Functionalities: minimal (I/O) => maximal (Random-Access) Random-Access Iterators
 For example, iterators for vector/deque support container.begin()+n, while itr + distance;
iterators for set/map only support ++ (efficiency reasons) itr += distance; std::advance(itr, distance);
 Categories: itr1 < itr2; std::distance(itr1, itr2); itr2 - itr1;
itr[myIndex]; *(itr + myIndex);
 Output Iterators: *itr = value, referred object is write-only, ++, no --, +=, -
 Input Iterators: value = *itr, referred object is read-only, ++, no --, +=, - Bidirectional Iterators
--itr;
 Forward Iterators: both *itr = value and value = *itr, ++ is OK but not --
 Bidirectional Iterators: iterators of map/set/list, ++, -- are OK but not +=, - Forward Iterators
 Random-Access Iterators: iterators of vector/deque, ++, --, +=, -, <, >, +, [] Input Iterators Output Iterators
 If an algorithm requires a Forward Iterator, you can provided it with val = *itr; *itr = val;
++itr; ++itr;
a Forward/Bidirectional/Random-Access iterator.
 If an algorithm demands an Input iterator, it guarantees that the
container pointed by the Input iterator is read-only by this algorithm.
9 10

Reordering Algorithms Reordering Algorithms (cont’d)


 sort // random-access iterators  reverse // bidirectional iterators
 sort(myVector.begin(), myVector.end());
 reverse(myVector.begin(), myVector.end());
// i.e. vector or deque only, cannot sort list, set or map
// each element must provide operator< or comparison function  random_shuffle // random-access iterators
 ex.bool compStrLen(const string &one, const string &two) { // or pass by value  random_shuffle(myVector.begin(), myVector.end());
return one.length() < two.length();
}
 shuffle (C++11) // random-access iterators
use pair to do multifield comparison
sort(myVector.begin(), myVector.end(), compStrLen);  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
shuffle(myVec.begin(), myVec.end(), std::default_random_engine(seed));
 stable_sort, partial_sort, partial_sort_copy, is_sorted, nth_element
 partition // bidirectional iterators  rotate // forward iterators
 bool isOdd(int i) { return (i%2)==1; }  rotate(v.begin(), v.begin()+2, v.end()); // begin, middle, end
vector<int> myvector; // (0, 1, 2, 3, 4, 5) => (2, 3, 4, 5, 0, 1)
for (int i=1; i<10; ++i) myvector.push_back(i); // 1 2 3 4 5 6 7 8 9  next_permutation // bidirectional iterators, operator<
vector<int>::iterator bound =  int v[] = {1, 4, 2};
std::partition(myvector.begin(), myvector.end(), isOdd); next_permutation(v, v+3); // (1, 4, 2) => (2, 1, 4)
// possible result: 1 9 3 7 5 6 4 8 2
 stable_partition, is_partitioned11  prev_permutation find next with carry
bound 12
Other Utilities Other Utilities (cont’d)
 min(a,b)  max(a,b)  set_union // union of 2 sorted ranges, input iterators, operator<
 return the smaller one of a and b  return an output iterator that is the end of the constructed sorted ranges
 cout << min(2,1) << ' ' << min(3.5, 2.1) << ' ' << min('d', 'b'); //1 2.1 b  int a[] = {10,5,15,25,20}; int b[] = {50,40,30,20,10}; vector<int> c(10);
 min_element // forward iterators, operator< sort(a, a+5); sort(b, b+5); // 5 10 15 20 25 30 40 50 0 0
 return the iterator of the smallest element in range [first, end) vector<int>::iterator endIter = set_union(a, a+5, b, b+5, c.begin());
bool myfn(int i, int j) { return i<j; }
  max_element cout << *(endIter-1) << endl; // 50
struct { bool operator() (int i,int j) { return i<j; } } myobj; c.resize(endIter-c.begin()); // 5 10 15 20 25 30 40 50
int myints[] = {3,7,2,5,6,4,9};  set_intersection // intersection of 2 sorted ranges, input iterators, operator<
cout << *min_element(myints,myints+7); // 2, operator<  vector<int>::iterator endIter = set_intersection(a, a+5, b, b+5, c.begin());
cout << *min_element(myints,myints+7,myfn); // 2 cout << *(endIter-1) << endl; // 20 // 10 20 0 0 0 0 0 0 0 0
cout << *min_element(myints,myints+7,myobj); // 2 c.resize(endIter-c.begin()); // 10 20
 merge // sorted range, input iterators, operator<  bitset
 int a[] = {10,5,15,25,20}; int b[] = {50,40,30,20,10}; vector<int> c(10);  bitset<5> foo(string("01011"));
sort(a, a+5); sort(b, b+5); foo[0] = 0; /* LSB 01010 */ foo[1] = foo[0]; /* 01000 */
// bidirectional iterators
merge(a, a+5, b, b+5, c.begin()); foo.flip(1); /* 01010 */ foo.flip(); /* 10101 */
// [first, middle) + [middle, last)
 inplace_merge(first, middle, last) // sorted ranges, operator< cout << foo << ' ' << boolalpha << foo.test(3) << ' ' << foo.count() // 10101 false 3
13 14

#include <queue>
Max Heap std::priority_queue<int>
push()
Searching Algorithms
 Maintain a max heap in a vector or deque empty(), top(), pop()  InputItr find(InputItr first, InputItr last, const Type& value)
 creation (heapify): make_heap // random-access iterators, operator<  search for value in designated range [first, last)
 extract maximum: pop_heap  return an iterator to the first element found; use a while loop to find all
 does not remove maximum element from the container  returns last iterator if nothing found
 move the maximum to the end of the range, use v.pop_back() to remove it  if (find(myVec.begin(), myVec.end(), 137) != myVec.end()) …
 does not return anything, use v.front() beforehand or use v.back() afterward
 avoid using find() on set or map, use set::find() or map::find() for efficiency
 insert element: push_heap
1. v.push_back() to append the element, 2. push_heap() to sift-up 30 20 10 15 5  iterator_traits<InputItr::difference_type> count(InputItr first,
 sort the heap: sort_heap 10  30 InputItr last, const Type& value)
 return the number of elements x == value in the designated range [first, last)
int myints[] = {10,20,30,15,5}; 20 30 20 10
vector<int> v(myints,myints+5); 15 5 15 5  bool binary_search(RandItr first, RandItr last,

 make_heap(v.begin(),v.end()); const Type& value)
cout << v.front() << endl; // 30 20  search for value in the designated sorted range, [first, last), delineating by two
20
 pop_heap(v.begin(),v.end()); v.pop_back();  random-access iterators, i.e. iterators of vector or deque (map or set are sorted,
15 10
17 10 their find() are efficient, i.e. log n)
 v.push_back(17); push_heap(v.begin(),v.end()) 5 20 15 10 5  return true if found; false otherwise
5 15
sort_heap(v.begin(),v.end()); // no longer a heap  if (binary_search(myVec.begin(), myVec.end(), 137)) … // found
20 17 10 5 15 15 16
Searching Algorithms (cont’d) Searching Algorithms (cont’d)
 ForwardItr lower_bound(ForwardItr first, ForwardItr last,  ForwardItr search(ForwardItr first1, ForwardItr last1,
const Type& value) ForwardItr first2, ForwardItr last2)
 Find the first element x  value in the designated sorted range [first, last)  Searches the range [first1, last1) for the first occurrence of the subsequence
 itr = lower_bound(myVec.begin(), myVec.end(), 137); defined by [first2, last2), operator== is required
 return an iterator to the first element satisfying x  value  returns an iterator to its first element in [first1, last1), or last1 if no occurrence
 if (itr == last) … // all elements in [first, last) satisfy x < value is found.
else if (*itr == 137) … // 137 is found  e.g. [first1, last1) = (10, 20, 30, 40, 50, 60, 70, 80)
else … // *itr > 137 [first2, last2) = (40, 50, 60, 70)
Invoking search(first1, last1, first2, last2) returns first1+3
 ForwardItr upper_bound(ForwardItr first, ForwardItr last,  ForwardItr search_n(ForwardItr first, ForwardItr last,
const Type& value) Size n, const Type& value)  find first subsequence of n value’s
 Find the first element x > value in the designated sorted range [first, last)
 bool includes(InItr first1, InItr last1, InItr first2, InItr last2)
 itr = upper_bound(myVec.begin(), myVec.end(), 137);
 Two sorted ranges [first1, last1), [first2, last2)
 return an iterator to the first element satisfying x > value
 Returns whether every elements in [first2, last2) is also in [first1, last1)
both algorithms are O(log n)  e.g. (1,2,3,4,5) includes (2,4)
17 18

Six Parts of STL Iterator Adaptors - <iterator>


Iterator adaptor does not actually point to elements in a container.
less It helps inserting/extracting elements from a container or stream.
Algorithms Functors equal
greater  ostream_iterator<type> / ostreambuf_iterator<char>: formatted /
Adaptable function unformatted output iterator, attached to an ostream, use dereference
operator to write data to the output stream, useful to STL algorithms
Iterators Adapters ostream_iterator<int> myItr(cout, " "); *myItr = 123; myItr++;
i(o)stream_iterator vector<int> myVec;
back(forward)_insert_iterator copy(myVec.begin(), myVec.end(), ostream_iterator<int>(cout, "\n"));
reverse_iterator
 istream_iterator<type> / istreambuf_iterator<char>: formatted /
Allocators Containers unformatted input iterator, attached to an istream, use dereference
stack, queue
operator to read data from the input stream, useful to STL algorithms
 Containers rely on the allocators for memory and support iterators copy(istreambuf_iterator<char>(cin), istreambuf_iterator<char>(),
 Iterators can be used intimately in conjunction with the algorithms. ostreambuf_iterator<char>(cout) ); // one line stream copy
istream_iterator<int> itr(cin), endItr;
 Functors provide special extensions for the algorithms. int x; do x = *itr++, cout << x; while (itr!=endItr); 1 2 3 4^Z
 Adapters can produce modified functors, iterators, and containers. Note: endItr marks the end, is not attached to any input stream 1234 20
Iterator Adaptors - <iterator> Iterator Adaptors - <iterator>
 Many STL algorithms take in ranges of data and produce new data  set does not support front_insert_iterator or back_insert_iterator,
ranges as output. The results are overwritten to the destination. You only supports insert_iterator<Container> iter(container, iterator)
must ensure that the destination has enough space to hold the results. set<int> result;
 back_insert_iterator<Container>, vector, deque, list set_union(set1.begin(), set1.end(), set2.begin(), set2.end(),
inserter(result, result.begin()));
front_insert_iterator<Container>, and deque, list
// the 2nd argument is an iterator pointing to the insertion point
insert_iterator<Container> are output iterator adaptors simulated // does not make sense for set or map, but is meaningful for vector, deque, or list
with container’s push_back(), push_front(), and insert() members  Summary back_insert_iterator<vector<int> > itr(myVector);
back_insert_iterator<deque<char> > itr = back_inserter(myDeque);
 vector<int> myVector; // no need to allocate space beforehand
front_insert_iterator<deque<int> > itr(myIntDeque);
back_insert_iterator<vector<int> > itr(myVector); front_insert_iterator<deque<char> > itr = front_inserter(myDeque);
insert_iterator<set<int> > itr(mySet, mySet.begin());
for (int i=0; i<10; ++i) *itr++ = i; insert_iterator<set<int> > itr = inserter(mySet, mySet.begin());
int x[] = {10, 11, 12}; 0,1,2,3,4,5,6,7,8,9,12,11,10 ostream_iterator<int> itr(cout, " "); ostream_iterator<char> itr(cout);
ostream_iterator<double> itr(myStream, "\n");
reverse_copy(x, x+3, itr); formatted istream_iterator<int> itr(cin);
// reverse_copy(x, x+3, back_insert_iterator<vector<int> >(myVector)); istream_iterator<int> endItr; // Special end of stream value
ostreambuf_iterator<char> itr(cout); // Write to cout
// reverse_copy(x, x+3, back_inserter(myVector)); // a template function unformatted istreambuf_iterator<char> itr(cin); // Read data from cin
copy(myVector.begin(), myVector.end(), ostream_iterator<int>(cout,","));21 istreambuf_iterator<char> endItr; // Special end of stream value
22

Removal Algorithms Removal Algorithms (cont’d)


 Removal algorithms do not remove elements from containers; they  remove_copy, remove_copy_if
only shuffle down all elements that need to be erased.  copy the elements that aren't removed into another container, operator==
 They accept range specified by iterators, not containers, and thus do not know int myints[] = {10,20,30,30,20,10,10,20}; // 10 20 30 30 20 10 10 20
how to erase elements from containers. vector<int> myvector(8); // must ensure large enough
 They return iterators to the first element that needs to be erased. vector<int>::iterator iter = iter
remove_copy(myints, myints+8,
 ex1 int x[] = {218, 137, 130, 149, 137, 255}; myvector.begin(), // 10 30 30 10 10 0 0 0
vector<int> myvec; 218 130 149 255 137 255 20);
copy(x, x+6, back_inserter(myvec)); myvector.erase(iter, myvector.end()); // 10 30 30 10 10
myvec.erase(remove(myvec.begin(), myvec.end(), 137), myvec.end());  unique_copy
copy(myvec.begin(), myvec.end(), output_iterator<int>(cout, " "));
 returns an iterator pointing to the end of the copied range,
Output: 218 130 149 255 Note: myvec.erase(myvec.end()) causes runtime error which contains no consecutive duplicates.
myvec.erase(myvec.end(), myvec.end()) is fine
 ex2 string stripPunctuation(string input) { empty range
int myints[] = {10,20,20,20,30,30,20,20,10};
input.erase(remove_if(input.begin(), input.end(), ::ispunct), input.end()); vector<int> myvector(9); // 0 0 0 0 0 0 0 0 0
vector<int>::iterator iter = iter
return input;
} unique_copy(myints, myints+9, myvector.begin()); // 10 20 30 20 10 0 0 0 0
<cctype> 23 24
Functional Thinking Functional Thinking (cont’d)
 Note: You are not going to master STL <algorithm>, <numeric>, <iterator>, or  Functional Thinking: Paradigm over Syntax, Neal Ford, 2014
<functional> through your procedural or object-oriented intuitions!!!
 Becoming Functional: Steps for transforming to a functional programmer,
Joshua Backfield, 2014
 Cede control over low-level details to the language/runtime (e.g., use
automatic garbage collection, automatic parallelism, iteration of
containers, control of iterations)
Can you figure out the way to use a  Prefer higher-level abstractions (highly optimized machineries)
chainsaw in place of the ax if what customized with callable objects together operated upon key data
you ever seen is an ax in working!!!! structures (generic containers),
 Common building blocks: filter, fold/reduce/search/selection, map/
sort/partition, closures (lambda expressions)
 Rooted on Lambda calculus. Prefer immutables and construct
Paradigm shift!!! programs with expressions like real mathematical functions. Avoid
mutable state (the moving parts) or subroutine-like processes.
 Stop thinking of low-level details of implementation and start focusing
on the problem domain and on the results across steps (gradual
25
transformation of input data toward the results) 26

Functional Thinking (cont’d) Again, why going FP?


 Michael Feather
OO makes codes understandable by encapsulating moving parts,  Cloud computing: Google Map/Reduce
FP makes codes understandable by removing moving parts.  Big Data: Statistics Computing Language – R
 General characteristics of functional programming
 Cool !!!  Fashion !!!
 Avoid mutable state, stateless transformation of immutable things
 Recursion
 Higher-order functions, partial functions, currying  Behind all these:
 Function composition the real motivation is paralellism:
 Lazy evaluation  Multicore Computing
 Pattern matching  GPU and Heterogeneous Computing
 Cloud and Distributed computing
 Functional Languages: Common LISP, ML, Scheme, Erlang, Haskell, F#
 Java-based: Scala, Clojure, Java8, Groovy, Functional Java
 Languages adopting FP paradigms: Perl, PHP, Ruby on Rail, JavaScript,
 Inherent immutability of Functional programming is a
Python, R, C#, and C++ very good starting point for exploiting H/W parallelism.
27 28
Map / Filter / Reduce Mapping Algorithms
STL Functional Language  transform: applies a unary function to a range of elements and stores
the result in the destination range (or 2 ranges for a binary function)
 transform() --- map in Scala or Closure
copy() string convertToLowerCase(string text) { 。。。
collect in Groovy transform(text.begin(), text.end(), text.begin(), ::tolower);
for_each() ::tolower
replace() return text;
} inplace 。。。
sort()
partition() int toInt(int ch) { return ch>='a' ? ch - 'a' : ch - 'A' ; }
string convertToInteger(string text, vector<int> &dest) {
 remove() + erase() --- filter in Scala or Closure transform(text.begin(), text.end(), back_inserter(dest), toInt);
return text;
 accumulate() --- reduce in Scala or Closure } the result could be another type
count() convert  for_each: applies a function to a range of elements
equal()
collect in Java8 void toLower(int &ch) { ch = ch<='Z' ? ch - 'A' + 'a' : ch; }
search() string convertToLowerCase(string text) {
selection() call-by-value parameter
inject, join in Groovy for_each(text.begin(), text.end(), toLower);
min_element() fold in Functional Java return text; for_each returns the copied function object
29
} 30

Mapping Algorithms (cont’d) for_each


 replace(ForwardItr start, ForwardItr end, #include <iostream>
const Type & toReplace, const Type& replaceWith) #include <algorithm>
using namespace std; int main()
int myints[] = {10, 20, 30, 30, 20, 10, 10, 20}; { int main()
vector<int> myvector(myints, myints+8); // 10 20 30 30 20 10 10 20 template <class T>
int
{ array[] = {1, 4, 2, 8, 5, 7};
replace(myvector.begin(), myvector.end(), 20, 99); // 10 99 30 30 99 10 10 99 class print {
const int n = sizeof(array)
int array[] / sizeof(int);
= {1, 4, 2, 8, 5, 7};
public:
 replace_if(ForwardItr start, ForwardItr end, const int n = sizeof(array) / sizeof(int);
print(ostream &os) print<int> f =
Predicate fn, const Type& replaceWith) m_os(os){}m_count(0) {}
: m_os(os), for_each(array, array+n, print<int>(cout));
print<int>(cout));
bool isOdd (int i) { return ((i%2)==1); } void operator()(const T &t) { cout << endl << f.count()
int myints[] = {10, 11, 30, 30, 13, 10};
m_os << t << ' '; << "0;objects printed." << endl;
return
vector<int> myvector(myints, myints+8); // 10 11 30 30 13 10
replace_if(myvector.begin(), myvector.end(), isOdd, 0); // 10 0 30 30 0 10 } ++m_count; }
private:
} return 0;
 generate(ForwardItr start, ForwardItr end, Generator fn) ostream
int &m_os;
count() { return m_count; } }
int randomNumber() { return (std::rand()%100); } };
private:
srand(unsigned(std::time(0))); vector<int> myvector(8); ostream &m_os;
generate(myvector.begin(), myvector.end(), randomNumber); int m_count;
 generate_n(OutputItr start, size_t n, Generator fn) };
31 32
STL Abstractions Things to Remember
 Containers abstract away the differences of underlying basic when using STL algorithms
types: the same vector implementation is used for ints or  Prefer a member function to a similarly named algorithm for
strings. An algorithm that handles elements in a vector does not performing a given task
concern the actual type stored in that vector. e.g. std::set::lower_bound() vs. std::lower_bound()
 Don't be afraid to use ordinary array pointers in a manner
 Iterators abstract away which container was used. For analogous to the use of iterators, where appropriate
example, the same random-access iterator implementation is e.g. int data[] = {5,4,2,3,1}; sort(data, data+5);
used for vector or deque and provides uniform interfaces to  You can generally use a more powerful iterator in place of a less
various algorithms. powerful one, if it is more convenient or "natural" to do so.
e.g. replace_n() requires output iterator, the ostream_iterator suffices, but the
 Algorithms (customized with the plugged-in function objects) bidirectional iterator of list/set/map is also good, let along the random-access
iterator of vector/deque
are abstract mechanisms that focus on solving the general
structure of a problem instead of the particular container or the  Ensure a container's size is large enough to accept all components
specific data type in the container. being transferred into it.
33 e.g. vector<int> v; back_insert_iterator<vector<int> > iter(v); 34

Palindrome Palindrome (cont’d)


 A palindrome is a word or phrase that is the same when read  2nd STL version: use reverse_iterator and equal
forwards or backwards, such as “racecar” or “Malayalam.”, ignoring
bool IsPalindrome(string input) {
spaces, punctuation, and capitalization. return equal(input.begin(), input.begin()+input.size()/2, input.rbegin());
 Procedural way }
bool IsPalindrome(string input) {
for (int k=0; k<input.size()/2; ++k)  More: stripping out everything except alphabetic char
if (input[k]!=input[input.length()-1-k]) #include <cctype> // isalpha()
return false; #include <algorithm> // remove_if(), equal()
return true; bool IsNotAlpha(char ch) {
} return !isalpha(ch);
string reversed;
 1st STL version reverse_copy(input.begin(), }
input.end(), bool IsPalindrome(string input) {
bool IsPalindrome(string input) {
reversed); input.erase(remove_if(input.begin(), input.end(), IsNotAlpha), input.end());
string reversed = input;
return reversed == input; return equal(input.begin(), input.begin()+input.size()/2, input.rbegin());
reverse(reversed.begin(), reversed.end());
}
return reversed == input; plain, narrative, but less efficient
}
35 36
Word Palindrome <cctype>
 Basic steps int isalnum(int c) Check if character is alphanumeric
1. Strip out everything except letters and spaces, convert to uppercase int isalpha(int c) Check if character is alphabetic
2. Break up the input into a list of words int isblank(int c) Check if character is blank (C++11)
3. Return whether the list is the same forwards and backwards int iscntrl(int c) Check if character is a control character
int isdigit(int c) Check if character is decimal digit
bool IsNotAlphaOrSpace(char ch) { return !isalpha(ch) && !isspace(ch); } int isgraph(int c) Check if character has graphical representation
bool IsWordPalindrome(string input) int islower(int c) Check if character is lowercase letter
{ int isprint(int c) Check if character is printable
input.erase(remove_if(input.begin(), input.end(), IsNotAlphaOrSpace),
int ispunct(int c) Check if character is a punctuation character
input.end());
int isspace(int c) Check if character is a white-space
transform(input.begin(), input.end(), input.begin(), ::toupper);
int isupper(int c) Check if character is uppercase letter
stringstream tokenizer(input);
vector<string> tokens; int isxdigit(int c) Check if character is hexadecimal digit
tokens.insert(tokens.begin(), istream_iterator<string>(tokenizer), int isalnum(int c) Check if character is alphanumeric
istream_iterator<string>()); int isalpha(int c) Check if character is alphabetic
return equal(tokens.begin(), tokens.begin()+tokens.size()/2, tokens.rbegin()); int tolower(int c) Convert uppercase letter to lowercase
} int toupper(int c) Convert lowercase letter to uppercase
In C++, a locale-specific template version of each function exists in header <locale>
Clang or GNU g++ has tolower()/toupper() in <locale> header also 37 use ::isalnum() to specify isalnum() in cctype 38

 Exponential and logarithmic functions


<cmath>
double exp(double x) Compute exponential function, e^x
 Trigonometric functions double frexp(double x, int* exp) Get significand and exponent, x=sign*2^exp

double cos(double) Compute cosine double ldexp(double x, int exp) x*2^exp


double sin(double) Compute sine double log(double x) Compute natural logarithm, w.r.t. Euler number e
double tan(double) Compute tangent double log10(double x) Compute common logarithm
double acos(double) Compute arc cosine double modf(double x, double* intpart) Break into fractional and integral parts
double asin(double) Compute arc sine double exp2(double x), exp2l(x) Compute 2^x (C++11)
double atan(double) Compute arc tangent double expm1(double x), expm1l(x) Compute e^x-1 (C++11)
double atan2(double) Compute arc tangent with two parameters
int ilogb(double x) Integer binary logarithm (C++11)
 Hyperbolic functions int ilogb(long double x) Returns the integral part of the logarithm of |x|, using
FLT_RADIX (==2) as base
double cosh(double) Compute hyperbolic cosine double log1p(double x) Compute logarithm plus one, log(x+1) (C++11)
double sinh(double) Compute hyperbolic sine
double log2(double x) Compute binary logarithm (C++11)
double tanh(double) Compute hyperbolic tangent
double logb(double x) Compute floating-point base logarithm, log|x| using
double acosh(double) Compute arc hyperbolic cosine (C++11)
FLT_RADIX (==2) as base (C++11)
double asinh(double) Compute arc hyperbolic sine (C++11)
double scalbn(double x, int n) scalbn(x,n) = x * FLT_RADIX^n (C++11)
double atanh(double) Compute arc hyperbolic tangent (C++11)
39 40
 Power functions
double pow(double base, double exp)
double sqrt(double x)
Raise to power, base^exp
Compute square root
Boost
double cbrt(double x) Compute cubic root (C++11)  Boost, an excellent open source C++ libraries, provides lots of
double hypot(double x, double y) Compute hypotenuse (C++11)
useful facilities not available in STL before C++11.
 Error and gamma functions  Boost ==> C++TR1 (Library extension to C++03) ==> C++11
double erf(double x) Compute error function (C++11)
 smart_ptrs – manage the lifetime of referred object with reference counting
double erfc(double x) Compute complementary error function (C++11)
(shared_ptr, shared_array, scoped_ptr, scoped_array, weak_ptr, intrusive_ptr)
double tgamma(double x) Compute gamma function (C++11)
 boost::lambda, boost::function, boost::bind – higher order programming
double lgamma(double x) Compute log-gamma function (C++11)
 boost::regex – regular expression
 Rounding and remainder: ceil, floor, fmod, trunc, round, lround, llround,  boost:: asio – blocking/non-blocking wait with timers, multithreading, socket
rint ,lrint, llrint, rearbyint, remainder, remquo  FileSystem – system independent file size, attributes, existence, directory
 Floating-point manipulation: copysign, nan, nextafter, nexttoward traversal, path handling
 Minimum, maximum, difference: fdim, fmax, fmin  template metaprogramming (boost::mpl)
 Other: fabs, abs, fma
 Classification: fpclassify, isfinite, isinf, isnan, isnormal, signbit Documents of Boost provide excellent in-depth discussions of the design
 Comparison: isgreater, isgreaterequal, isless, islessequal, islessgreater, decisions, constraints, and requirements that went into constructing the library.
isunordered
 Constants: INFINITY, NAN, HUGE_VAL 41 42

Magic Square Solver (1/4) Magic Square Solver (2/4)


 A “magic square” is a 3x3 grid in which all elements are distinct and  Output a configuration
all 3 elements in every row, column, and diagonal sum to the same for (i=0; i<3; ++i)
number, e.g. 2 7 6 2+7+6=15 copy(magicSquare.begin()+3*i, magicSquare.begin()+3*i+3,
9 5 1 2+5+8=15 ostream_iterator<int>(cout, " ")), cout << endl;
4 3 8 7+5+3=15

 A magic square can be represented as a linear vector of {1,2, …, 9}  Use for loop to evaluate everyone of the 8 conditions
const int starts[] = {0, 3, 6, 0, 1, 2, 2, 0};
2 7 6 9 5 1 4 3 8
const int offsets[] = {1, 1, 1, 3, 3, 3, 2, 4};
vector<int> magicSquare(9); for (i=0; i<8; ++i)
for (i=0; i<9; ++i) magicSquare[i]=i+1; for (sums[i]=0,j=0; j<3; ++j)
sums[i] += magicSquare[starts[i]+j*offsets[i]];
 Use next_permutation() to generate all configurations
do {  Use count() to validate the conjunction of all 8 conditions
ouputConfig(magicSquare); if (8==count(sums, sums+8, 15))
} outputConfig(magicSquare);
while (next_permutation(magicSquare.begin(), magicSquare.end()));
43 44
Magic Square Solver (3/4) Magic Square Solver (4/4)
 WHY not use accumulate() to replace the for loop? class Constraint {
 Forget the low-level details, right? public:
Constraint(const int start, const int offset)
 Problem: the argument passed from accumulate() to the predicate
is only value instead of the index : index(0), x1(start), x2(start+offset), x3(start+2*offset) {}
int operator()(int sum, int value) {
 It is necessary for the predicate to have state in order to
if (index==x1||index==x2||index==x3)
count the number of calls.
sum += value;
 It is necessary to config the predicate with different ways index++;
of accumulation. return sum;
}
use a functor private:
for (i=0; i<8; ++i) int index;
sums[i] = accumulate(magicSquare.begin(), magicSquare.end(), 0, const int x1, x2, x3;
Constraint(starts[i],offsets[i])); };
45 46

Monoalphabetic Substitution Cipher Substitution Cipher (cont’d)


 A monoalphabetic substitution cipher is a simple form of encryption, where each  Config transform with a decryption functor, which takes a decoding
of the 26 letters are mapped to another letter exclusively in the alphabet. codebook and maps a ciphertext character to a plaintext character.

A B C D E F G H I J K L MN O P Q R S T U VWX Y Z class mapping {


public:
encryption decryption
mapping(int table[]): DAT(table) {}
K V D Q J WA Y N E F C L R H UX I O G T Z P MS B char operator()(char &source) { return 'A'+DAT[source-'A']; }
private:
For example: plaintext “THECOOKIESAREINTHEFRIDGE” int *DAT;
ciphertext “GYJDHHFNJOKIJNRGYJWINQAJ” };
transform(plaintext1.begin(), plaintext1.end(),
 Use random_shuffle to generate a map as the encoding codebook and a map back_inserter(ciphertext), mapping(encTable));
as the decoding codebook
 Config transform with a decryption functor, which takes a decoding
codebook and maps a ciphertext character to a plaintext character.
int encTable[26], decTable[26]; // two direct address tables (DAT)
random_shuffle(encTable, encTable+26); transform(ciphertext.begin(), ciphertext.end(),
for (i=0; i<26; i++) decTable[encTable[i]] = i; back_inserter(plaintext2), mapping(decTable));
47 48
pair<T1, T2> Convenient Aliases
 pair is a simple, handy, and useful template of data structure used typedef vector<int> vi;
with any STL containers and algorithms. A two-dimensional 10x20 array initialized with 0
typedef vector<vi> vvi; vvi matrix(10, vi(20, 0))
template <typename T1, typename T2> struct pair { typedef pair<int,int> ii;
T1 first; T2 second;
};
typedef vector<string> vs;
typedef vector<ii> vii;
 Especially, it defines operator== and operator< that can be used
typedef vector<pair<double, ii> > vdii;
seamlessly with containers and reordering algorithms.
typedef vector<vii> vvii;
bool operator<(pair<T1,T2>& rhs) {
if (first<rhs.first) return true;
else if ((first==rhs.first)&&(second<rhs.second)) return true; #define sz(a) int((a).size()) only for GNU g++
else return false; // *this >= rhs #define pb push_back
} #define all(c) (c).begin(),(c).end()
 e.g. multi-field sorting on pair<sring, pair<int, vector<int> > > #define tr(c,i) for(typeof((c).begin() i = (c).begin(); i != (c).end(); i++)
vector<pair<string, pair<int, vector<int> > > v; #define has(c,x) ((c).find(x) != (c).end())
… // populate the vector v with data; #define hasG(c,x) (find(all(c),x) != (c).end())
sort(v.begin(), v.end()); 49

Graph Connectivity - DFS Graph Connectivity - BFS


 Depth-First Search  Breadth-First Search 0
vvi W; // global graph
0 Foxville 1 Steger
vi visited; 0 Foxville 1 Steger 1 2 4
int w[][6] = {{2,4},{3},{0,4,5}, 0 3 0 3 4 5 0 2 5
2 Lusk {1},{0,2,5},{2,4}};
3 2 Lusk 1 2 5 2 3 4
Springfield int wn[6] = {2,1,3,1,3,2}; 3
Springfield queue
int i, N = 6; 0 1 2 4 3 5
0 5 Del Rio for (i=0; i<N; i++)
4 Mystic 5 Del Rio visited 0 1 2 3 4 5
W.pb(vi(w[i],w[i]+wn[i])); 4 Mystic 10 01 01 01 01 01
1 2 4 vi visited(N, 0);
void dfs(int i) {
0 3 if (!visited[i]) { queue<int> Q;
1 2 5 visited
visited[i] = 1; visited[0] = 1; Q.push(0); // start vertex is 0
0 1 2 3 4 5
0 3 4 5 for_each(all(W[i]), dfs); while (!Q.empty()) { tr (W[i], it) // only for g++
0 01 01 01 01 01
1
} i = Q.front(); Q.pop();
0 2 5
} for (vi::iterator it=W[i].begin(); it!=W[i].end(); ++it)
2 3 4 if (!visited[*it]) visited[*it] = 1, Q.push(*it);
visited = vi(N, 0); dfs(0); // start from vertex 0 }
if (find(all(visited), 0) == visited.end()) cout << "Connected\n"; if (find(all(visited), 0) == visited.end()) cout << "Connected\n";
51 52
Dijkstra’s Shortest Path Dijkstra’s Shortest Path (cont’d)
 implementation with a priority_queue
int i, j, N = 6, w[][6][2] =
vi D(N, 0x7fffffff); // distance from start vertex to each vertex at the moment
{{{1,80},{2,40},{4,60}},
priority_queue<ii,vector<ii>,greater<ii> > Q; // min heap
{{0,80},{3,100}}, 0 Foxville 80 1 Steger
{{0,40},{3,20},{4,120},{5,60}}, 0 Foxville 80 1 Steger
40 D[4] = 0; Q.push(ii(D[4],4)); // start vertex: 4
{{1,100},{2,20},{5,120}}, 100 40
60 2 Lusk 20 int v, d, v2, cost; 100
{{0,60},{2,120},{5,40}}, 3 60 2 Lusk
while (!Q.empty()) { 20 3
{{2,60},{3,120},{4,40}}}; 120 120 Springfield ii top = Q.top(); Q.pop(); // min element
60 120 120 Springfield
int wn[6] = {3,2,4,3,3,3}; 40 60
v = top.second, d = top.first;
vvii G; // weighted graph 4 Mystic 5 Del Rio 40
if (d <= D[v]) { 5 Del Rio
vii tmp; 4 Mystic
cout << v << ':' << d << endl;
for (vii::iterator it=G[v].begin(); it!=G[v].end(); ++it) {
4:0
for (i=0; i<N; i++) { v2 = it->first, cost = it->second;
5:40
for (j=0; j<wn[i]; j++) if (d + cost < D[v2])
0:60
tmp.push_back(pair<int,int>(w[i][j][0],w[i][j][1])); D[v2] = d + cost, Q.push(ii(D[v2], v2));
2:100
G.push_back(tmp); } there could be multiple entries of the same 3:120
} } vertex in the priority queue, only the one that 1:140
53 } has the least distance ever seen is considered 54

Dijkstra’s Shortest Path (cont’d) References


 implementation with set
 The C++ standard library: a tutorial and reference, Nicolai. M.
vi D(N, 0x7fffffff); // distance from start vertex to each vertex at the moment Josuttis, 2nd ed., AW, 2012 (1st ed, 1999)
set<ii> S; set<ii>::iterator itS;  C++ 標準程式庫, 侯捷, 2002 (Josuttis, 1st ed.)
4:0
D[4] = 0; S.insert(ii(D[4],4)); // start vertex: 4
5:40
 Generic Programming and the STL - Using and Extending the C++
while (!S.empty()) { 0:60 Standard Template Library, by Matthew H. Austern, AW, 1998
ii top = *(S.begin()); S.erase(S.begin()); // min element 2:100  泛型程式設計與 STL, 侯捷/黃俊堯合譯, 碁峰, 2001
int v = top.second, d = top.first; 3:120  The Annotated STL Sources,
cout << v << ':' << d << endl; 1:140
 STL 源碼剖析, 侯捷, 碁峰, 2002
for (vii::iterator it=G[v].begin(); it!=G[v].end(); ++it) {
int v2 = it->first, cost = it->second;  Effective STL, by Scott Meyers, AW, 2001
if (D[v2] > d + cost) { // d==D[v] actually  Effective STL 簡中 , Center of STL study
if ((D[v2]!=0x7fffffff)&&(itS=S.find(ii(D[v2],v2)))!=S.end()))
 Modern C++ Design Generic Programming and Design Patterns
S.erase(itS);
D[v2] = d + cost; S.insert(ii(D[v2], v2));
Applied, Andrei Alexandrescu, AW, 2001
might be erased earlier
}  Designing Components with the C++ STL, 3rd ed., Ulrich
guarantees no duplicated entry
} with the same vertex in the set
Breymann, AW, 2002
} 55 56

You might also like