270 likes | 288 Views
Learn how to use STL algorithms in C++ independently of container types, operating via iterators with callbacks like function pointers, functors, and lambda expressions. Examples included.
E N D
STL Algorithms algorithms independent of containers
STL Algorithms Description • independent of container types: operate on iterators • operateon the 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>
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()functionin map and set is faster (logarithmic or constant for hashes), use it instead
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) • function references exist in C++ as well • 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; }
Lambda Expressions • anonymous function • defined in C++11 • syntax: [capture](parameters) -> returnType {body} • capture – passing discipline and (optionally) name of variables taken from outside of scope (called context) 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 copy (similar to value) variables are captured at the time of lambda definition (not invocation) • if returnTypeis omitted and there is a single return statement in body, compiler deduces return type on basis of return-expression • if no parameters, parentheses are optional • example: []{cout << ”Hello, World!” << endl;} • can be used as parameters for other functions, have to conform to signature
count_if, generate, for_each • count_if– counts number of elements that satisfy callback condition int num=55; intcnt = count_if(vect.begin(), vect.end(), [num](inti){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(), [](inti){cout << i << " ";});
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;});
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 MyFunctoraddOne(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
generate with Functor class MyFunctor{ public: MyFunctor(int x) : x_(x) {} int operator() () {return x_;} private: int x_; }; … MyFunctoraddIt(5); vector<int> v(10); generate(v.begin(), v.end(), addIt); what are the resultant values of v?
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
Comparison and Logical Functors • comparison functors: equal_tonot_equal_to less greater less_equalgreater_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_andlogical_orlogical_not • example: logical_and in accumulate to determine if all boolean elements are true vector<bool> flags; … boolallTrue=accumulate(flags.begin(), flags.end(), true, std::logical_and<bool>());
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
Using Binders to Form Callbacks • binders useful in forming callbacks for algorithims inline using namespace std::placeholders; boolpassingScore(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?
std::function • a number of similarly operating constructs: • lambdas (btw, only non-biding lambdas can be assigned to function pointers) • function pointers • functors • binders • std::function allows to point to any of them uniformly • needs functional header • signature is in angle brackets • examples: // lambda function<void(int)> fp = [](int x){cout << x << endl;}; void funcReg(int x){ // regular function cout << x << endl; } function<void(int)> fp = funcReg;
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
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});
Non-ModifyingSearch 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.cbegin(), vect.cend()); • 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
Non-ModifyingComparison 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.
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 • may move whole container • unique() – eliminates consequent duplicates – useful with sorted containers • generate_n() – fills elements by invoking callback n times, see later
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
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()
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.
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
Iterator Movement Functions poor man’s iterator arithmetic 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 • if iterator is input – repeatedly calls increment • if random access – adds position to iterator • 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
Iterator Adapters: Reverse • 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 • crbegin() and crend() also exist
Iterator Adapters: Insert • insert iterators – special type of output iterators designed for algorithms (such as copy_if) to insert rather than overwrite elements do not have to know target range • insert_iterator() • assignment calls insert(position, element)on container, advances insert iterator to the next position • initialized with container, position • inserter(container, position) returns insert_iteratoron 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_iteratorfor this container • front_insert_iterator() – calls push_front() • elements are inserted sequentially, may be inefficient • increment/decrement have no effect
Algorithms Review • what is half-open range? • what is a callback? function pointer? • lambda function? what is capture? capture block? capture by reference? capture by copy? • functor? advantages/differences with function pointer? • predefined functors? arithmetic? logical? • binder (function adapter)? binder in a callback? • std::function purpose/usage? • algorithm categories • utility • non-modifying: search, comparison, operational, numerical processing • modifying • sorting • set • iterator movement functions? • iterator adapters: inserters/reverse?