STL Algorithms algorithms independent of containers STL Algorithms Description • • • • independent of container types: operate on iterators – operate on half-open range of elements of a container specified by iterators often behavior can be modified through callbacks – references to code callbacks – function pointers (C-style) – function objects (functors) – lambda expressions (C++11) most algorithms are declared in <algorithm>, some are in <numeric> 2 Algorithm Example: find • looks for specific element in range • needs <algorithm> • find(beginRange, endRange, toFind) • • • returns iterator to first matching element in range of the container – if associative – not necessarily first, use lower_bound if not found – returns endRange (past last element) linear complexity find() method in map and set is faster (logarithmic), use it instead find() method in list should be used instead 3 find_if with Function Pointer Callback • find_if(beginRange, endRange, condition) • • • • returns iterator to first element in range satisfying condition third parameter is a callback may be the name of a function (function pointer) – need to accept element type – need to be a predicate (return boolean) example: vector<int> vect; … auto it=find_if(vect.begin(), vect.end(), moreThan5); … bool moreThan5(int elem){ return elem>5; } 4 Lambda Expressions • • • anonymous function defined in C++11 syntax: [capture] -> returnType (parameters){body} • capture – passing discipline and (optionally) name of variables taken from outside of scope of the lambda expressions – [] no variables defined. Attempting to use any external variables in the lambda is an error – [x, &y] x is captured by value, y is captured by reference – [&] any external variable is implicitly captured by reference – [=] any external variable is implicitly captured by value • compiler may be able to deduce return type from return statement: returnType is optional • • if no parameters, parentheses are optional example: []{cout << ”Hello, World!” << endl;} • can be assigned to function pointer variables – watch out for implicit captures can be used as parameters for other functions, have to conform to signature • 5 count_if, generate, for_each • count_if – counts number of elements that satisfy callback condition int num=55; int cnt = count_if(vect.begin(), vect.end(), [num](int i){return i==num;}); • generate – fills elements with value returned by callback generate(vect.begin(), vect.end(), []{return rand()%10;}); • for_each – executes callback for each element for_each(vect.begin(), vect.end(), [](int i){cout << i << " ";}); 6 accumulate with various callbacks • • accumulates data about container two forms accumulate(beginRange, endRange, initialValue) – sums elements, sum initialized to initialValue, returns accumulated value accumulate(beginRange, endRange, initialValue, callback) invokes callback with two arguments, first is accumulator • callback can be function or lambda int product(int num1, int num2){ return num1 * num2; } double mult = accumulate(vect.begin(), vect.end(), 1, product); or double multLambda = accumulate(vect.begin(), vect.end(), 1, [](int num1, int num2){return num1 * num2;}); 7 Function Objects (Functors) • • functor – object that may be invoked as a standalone function done by overloading operator() may have any number of arguments and return any value class MyFunctor{ public: MyFunctor(int x) : x_(x) {} int operator() (int y) {return x_+y;} private: int x_; }; … • then invoke like a standalone function MyFunctor addOne(1); // creating a functor object cout << addOne(2) << endl; // call it like a regular function • may keep state between calls. – use this with caution for algorithms as implementations are free to copy/invoke out of order/invoke concurrently lambdas provide a convenient alternative • 8 Predefined Functors, Arithmetic • • • • STL provides a number of predefined functors defined in <functional> in std namespace (need to be imported or scope resolved) arithmetic: plus, minus, multiplies, divides, modulus • have to be instantiated with type plus<int> myPlus; int result = myPlus(3,4); cut << result << endl; • may be used in algorithms as callbacks int sum = accumulate(vect.begin(), vect.end(), 0, plus<int>()); • regular operators cannot be used as callbacks, functors are adapters that wrap regular arithmetic operators 9 Comparison and Logical Functors • comparison functors: equal_to not_equal_to less greater less_equal greater_equal – less is used as default comparison in priority_queue container adapter – may be changed, have to specify container, usually vector – example: reversing sorting order in priority_queue priority_queue<string, std::vector<string>, std::greater<string>> workWeekR; • logical functors: logical_and logical_or logical_not – example: logical_and in accumulate to determine if all boolean elements are true vector<bool> flags; … bool allTrue=accumulate(flags.begin(), flags.end(), true, std::logical_and<bool>()); 10 Function Adapters (Binders) • • binder (function adapter) – a specialized function that creates a function by assigning (binding) a value of parameter of another function bind() a C++11 feature – most flexible binder newFunction bind(oldFunction, arguments) where – newFuncton – pointer to new function with bound parameters – oldFunction – old function – arguments – arguments to old function, in old-function parameter order • free specified as _1 _2, etc defined in std::placeholders namespace • • • bound auto is useful as return type or it gets complicated examples auto f1 = bind(myFunc, _1, str); // binds second parameter to string str auto f2 = bind(myFunc, _2, _1); // swaps parameters 11 Using Binders to Form Callbacks • binders useful in forming callbacks for algorithims inline using namespace std::placeholders; bool passingScore(int s, int threshold){ return s>=threshold; } ... // biding second argument of function passingScore to 70 auto it=find_if(vect.begin(), vect.end(), bind(passingScore, _1, 70)); // binding second argument of standard functor greater_equal auto it = find_if(vect.begin(), vect.end(), bind(std::greater_equal<int>(), _1, 70)); • last example is probably easier to read with lambda, how would you implement it? 12 Containers of Objects • algorithms perform on non-basic types correctly provided that proper operators are defined – e.g. operator< for sorting, operator== for searching • common task: invoke a method on each object – suppose myclass declares myfunc() method and container cont holds elements of myclass may be accomplished as follows for_each(cont.begin(), cont.end(), &myclass::myfunc); – if need to pass parameters, use bind() for_each(cont.begin, cont.end(), bind(&myclass::myfunc, _1, value)); 13 Algorithm Categories • • utility – not operating on containers but useful non-modifying – not updating the container – search: min_element, max_element, find_first_of, search, search_n – comparison: equal, mismatch, lexicographical_compare – operational: for_each – numerical processing: count, count_if, accumulate • • • modifying – updating the container sorting – sorting or (dis)ordering container set – set functions 14 Utility Algorithms • min, max, minmax, swap • • operate on a couple of elements use operator< • • use function templates examples int x=1,y=2; cout << min(x, y); // prints1 cout << max(x, y); // prints 2 swap(x, y); cout<< x << y; // prints 21 auto pair = minmax(x,y); cout << pair.first << pair.second; // prints 12 • In C++11, utility algorithms operate on initializer lists: max({1,2,3,4,5}); 15 Non-Modifying Search Algorithms • • • • • • • • • • • return iterator to first element found accept range by default use opeator== or operator< find, find_if, find_if_not – already covered min_element, max_elment – locate element auto it=min_element(vect.begin(), vect.end()); adjacent_find – finds the first pair of matching consecutive elements find_first_of – finds first occurrence of elements in target range search – finds target subsequence search_n – finds consecutive elements searches that work on ordered sequences (sorted vector, map, multimap, set, multiset): binary_search, lower_bound, upper_bound, equal_range C++11 functions: find_if_not, minmax_element, all_of, any_of, none_of 16 Non-Modifying Comparison and Operational • • comparison – compare entire ranges of elements equal() – returns true if elements in both ranges are equal mismatch() returns iterator to the first mismatched element lexicographical_compare() – dictionary order comparison of elements operational for_each() – executes callback on each element of the range: may print a copy of every element, accumulate info about all elements, etc. 17 Modifying Algorithms • • • • • • • • • usually operate on two ranges: source range and destination (target) range, ranges may be independent, overlapping or the same (in place operation) transform() – similar to for_each() expects callback to return a value to be stored in the target range – variant: has two source ranges, callback accepts two parameters – one for each source range and stores value for the target range. Can be used to process two containers – the target may be one of the source rages copy() – copies source to target range copy_if() – copies if callback returns true C++11 – returns iterator past the last element copied – can be used to trim unused after copy elements replace() – replaces elements with particular value with a different one replace_if() – replaces by new value if callback returns true reverse() – reverses elements in container move() – moving elements with C++11 move semantics, leaves source elements in unspecified but valid state unique() – eliminates consequent duplicates – useful with sorted containers 18 Modifying: Remove (and Erase) • • remove() – removes elements with specific value remove_if() – removes if callback returns true • • both modifying algorithms do not erase elements from containers (do not know if whole or full range) – instead move remaining elements forward – return iterator past last remaining elements remove-erase-idiom – get the returned iterator and then use the container’s erase() function to eliminate removed elements • • – can be done in single line removes are linear – preferred to iterative erase() invocation – for random access containers memory reorganization to keep continuous, results in quadratic complexity 19 Sorting • • • • • sort() – n log(n) sort of the range merge() – linear merge of sorted source ranges – target range has to be large enough – does not return iterator; no elements are removed – number of elements in target container is sum of source sizes: use resize() or erase() to trim target unique() – eliminates duplicates, returns iterator past the last element binary_search() – log(n) search in sorted container for a value, returns true if found – lower_bound() is same complexity but more useful random_shuffle() – reshuffles range in linear time, internally uses rand() 20 Set operate on sorted containers with unique elements, not necessarily sets; in fact, sequential containers are recommended • includes() – returns true if first range includes second • set_union() – computes union (duplicates eliminated) from two source ranges, puts it in destination range, returns pointer past last element • set_intersection() – computes intersection of two source ranges • set_difference() – difference (complement) of first range with second – elements of first range that are not present in the second • set_symmetric_difference() – elements of first range that are not present in second and v.v. 21 Enhanced Iterator Functions further enhance the power of algorithms • iterator movement functions – makes iterator operate as random access regardless of type • reverse iterators – iterate in reverse direction • inserters – target range does not have to match source range 22 Iterator Movement Functions implemented as templates • advance(iterator, position) – move iterator, position elements, returns void, iterator needs to be at least input – if iterator is input – moves copy of iterator by repeatedly calling increment – if iterator is random access – calls operator+(position) • next(iterator, position) – returns iterator pointing position elements forward, original iterator is not modified • prev(iterator,position) - same as next in reverse direction • distance(firstIterator, lastIterator) – returns the number of elements between firstIterator and lastIterator. If iterators are random access, uses subtract, otherwise repeatedly calls increment++ 23 Iterator Adapters • declared in <iterator> • reverse_iterator – iterates in reverse order (increment advances backward) – rbegin() – returns reverse iterator starting at the last element of container – rend() – returns iterator before the first element – base() – returns underlying iterator plus one – useful to determine distance to beginning • insert iterators – special type of output iterators designed for algorithms (such as copy) to insert rather than overwrite elements – insert_iterator() – calls insert(position, element) on container • is initialized with container, position • inserter(container, position) returns insert_iterator on this position • useful for associative containers whose keys are usually not modifiable on iteration – back_insert_iterator() – calls push_back() on container • back_inserter(container) returns back_insert_iterator for this container – front_insert_iterator() – calls push_front() 24