The STL The heart of the STL is a collection of algorithms and data structures Each algorithm works...

68
The STL • The heart of the STL is a collection of algorithms and data structures • Each algorithm works with a large number of data structures • If you create a new data structure it will work with the algorithms • If you create a new algorithm is will work with the data structures • It introduces some new concepts to allow this
  • date post

    21-Dec-2015
  • Category

    Documents

  • view

    225
  • download

    0

Transcript of The STL The heart of the STL is a collection of algorithms and data structures Each algorithm works...

The STL• The heart of the STL is a collection of

algorithms and data structures

• Each algorithm works with a large number of data structures

• If you create a new data structure it will work with the algorithms

• If you create a new algorithm is will work with the data structures

• It introduces some new concepts to allow this

Interface

• How do the algorithms use the data structures?

• The data structures provide a consistent way of access

• We call them Iterators

Designing the Interface

• An interface which is as generic as possible is wanted

• It should work with arrays - since they are probably the simplest container

• It should work with linked lists

• It should work with trees and hash tables

Iterating through an array

for (int index=0;index<length;index++)// do something with array[index]

for (T* ptr = array; ptr!=&array[length];ptr++)//do something with *ptr

Iterating through a linked list

for (Node *node=list.head; node!=0;node=node->next)

// do something with node->value

Generic Iterating

• Set current element to first element

• If current element is not past the end

• Do something with the current element

• Set current element to the next element

STL Iterators

• The key to the STL is understanding that it is designed to work with arrays

• Arrays are built in to the language

• You can’t inherit from them - so using OO inheritance to write generic algorithms won’t work

• You can’t change the way arrays work - so you need to be compatible with them

The Basic Pointer Operations

• *ptr - used to access what is pointed to

• ptr++, ++ptr - used to advance to the next element

• ptr == ptr2, ptr != ptr2 - used to check if two pointers point to the same element

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;

}

• Note that the following will work:int array[5] = {1,2,3,4,5};cout << *(find(array,array+5,3)); //not safe!

Requirement, Concepts, Models

• A concept is a set of requirements

• A requirement is some operation doing a useful thing

• A model, is something that fulfills a concept.

• An Iterator is a concept

• A pointer is a model of an Iterator.

Basic Concepts• The fundamental concepts, that seem too obvious to

state

• Assignable Possible to copy values

Possible to assign new values

• Default Constructable Possible to construct an object with no arguments

T() creates an object T t; declares a variable

Basic Concepts Continued

• Equality Comparable Possible to compare two object for equality

x == y and x != y must do so

• LessThan Comparable Possible to test if one object is less than or greater than

another

x<y ,x>y, x<=y, x>=y must do so

• A regular type models all the above in a consistent fashion

Input Iterator

• The simplest Iterator

• Three kinds of values: dereferencable, past the end, and singular

• Possible to compare for equality

• Possible to copy and assign

• Possible to dereference to get associated value type

• Possible to increment with ++i and i++

Input Iterators

• That is all, for example, the following do not have to be possible Modify the dereferenced value

Other comparison operators

Read a value more than once

Output Iterators

• The other simple Iterator

• Possible to copy and assign

• Possible to write a value with *i = x• Possible to increment

• (Again, writing twice isn’t required, testing for equality isn’t required, etc)

Using Iterators Example

template <typename InputIterator, typename OutputIterator>

OutputIterator copy(InputIterator first,InputIterator last,OutputIterator result) {

for (;first!=last;++result,++first)*result = *first;

return result;}

Iterator Implementation Example

template <typename T>class ostream_iterator {public:ostream_iterator(ostream& s,const char *c=0): os(&s),string( c ) {}ostream_iterator(const ostream_iterator& o): os(i.os), string(i.string) {}ostream_iterator& operator=(constostream_iterator &o) {os = i.os;string = i.string;return *this;}

ostream_iterator<T>& operator=(const T& value) {

*os << value;if (string) *os << string;return *this;

}ostream_iterator<T>& operator*() {

return *this;}ostream_iterator<T>& operator++() {

return *this;}ostream_iterator<T>& operator++(int) {

return *this;}private:

ostream *os;const char* string; };

Forward Iterators

• Both an InputIterator and an OutputIterator

• Allows multi-pass algoithms

• p=q; ++q; *p = x; works

Bidirectional Iterators• Does everything a ForwardIterator does.

• Also supports decrement template <typename BidirectionalIterator,

typename OutputIterator >

OutputIterator reverse_copy( BidirectionalIterator first, BidirectionalIterator last,

OutputIterator result) {while (first != last)

*(result++) = *(--last);return result;

}

Random Access Iterators• Supports everything that a Bidirectional Iterators does

• Supports random access Addition and subtraction i + n and i - n

Subscripting i[n]

Subtraction of iterators i1 - i2

Ordering i1 < i2

• Random access must be in constant time

• Basically acts just like a real pointer

Ranges

• Iterators are often used in ranges

• A range is a start and end iterator

• The start iterator is part of the range

• The end iterator is not

• In maths it’s [start, end)

• The empty range is start==end

Containers Containers contain elements Container - accessed via Input Iterators

begin() and end() get iterators

• Forward Container - accessed via Forward Iterators Reversible Container - accessed via Bidirectional Iterators

rbegin() and rend() get reverse iterators

Random Access Containers - accessed via Random Access Iterators

More Container Types

There are some other Container types Sequence

Refinement of Forward Container

Can add or delete elements at any point

member functions: insert and erase

Associative Containers Every element has a key, by which it is looked up

Elements are added and deleted via keys

Sequence Abstractions

• Front Insertion Sequence Insertion at front in constant time

Access first element in constant time

• Back Insertion Sequence Append to end in constant time

Access last element in constant time

Associative Container Abstractions• Unique Associative Container

No two elements have the same key Conditional insertion

• Multiple Associative Container May contain multiple elements with the same key

• Simple Associative Container The elements are their own keys

• Pair Associative Container Associates a key with another object The value type is pair<const key, value>

Associative Container Abstractions Continued

• Sorted Associative Container Sorts elements by key in a strict weak ordering

• Hashed Associative Container Uses a hash table implementation

Vector

• Simplest container, often the most efficient

• Has both a size and a capacity

• Inserting elements can cause reallocation

• Reallocation invalidates iterators

• Random Access Container

• Back Insertion Sequence

Vector Usage Example

#include <iostream>#include <vector>#include <algorithm>#include <string>int main() {vector<string> v;

string s; while(cin>>s) v.push_back(s); copy(v.begin(),v.end(), ostream_iterator<string>(cout,"\n"));

}

List

• A doubly linked list

• Insertion and spliciing do not invalidate iterators

• Deletion only invalidates iterators at the deleted element

• Reversible Container

• Front Insertion Sequence

• Back Insertion Sequence

List Usage Example

#include <iostream>#include <list>#include <algorithm>#include <string>int main() {list<string> l;string s;while(cin>>s)

l.push_front(s);copy(l.begin(), l.end(), ostream_iterator<string>(cout,"\n"));

}

Deque

• A double-ended queue

• Insertion invalidates all iterators

• Deletion from middle invalidates all iterators

• Deletion at the ends invalidates iterators at the deleted element only

• Random Access Container

• Front Insertion Sequence

• Back Insertion Sequence

Deque Usage Example

#include <iostream>#include <deque>#include <algorithm>#include <string>int main() {deque<string> d;d.push_back("first");d.push_front("second");d.insert(d.begin()+1, "third");d[2] = "fourth";copy(d.begin(), d.end(),

ostream_iterator<string>(cout,"\n"));}

Set

• Maintains elements in sorted order

• Inserting does not invalidate iterators

• Deleting only invalidates iterators at the deleted element

• Sorted Associative Container

• Simple Associative Container

• Unique Associative Container

#include <iostream>#include <set>#include <algorithm>#include <string>int main() {int prime_a[5] = {2,3,5,7,11};int odd_a[5] = {1,3,5,7,9};int even_a[5] = {2,4,6,8,10};set<int> prime(prime_a,prime_a+5);set<int> odd(odd_a,odd_a+5);set<int> even(even_a,even_a+5);cout << "Union: ";set_union(prime.begin(), prime.end(),

even.begin(), even.end(),ostream_iterator<int>(cout, " "));

}

Multiset

• Inserting does not invalidate iterators

• Deleteing only invalidates iterators at the deleted element

• Sorted Associative Container

• Simple Associative Container

• Multiple Associative Container

Map

• Inserting does not invalidate iterators

• Deleting only invalidates iterators at the deleted element

• Sorted Associative Container

• Pair Associative Container

• Unique Associative Container

Map Usage Example

#include <iostream>#include <map>#include <string>int main(){map<string,int> days;days["January"] = 31;

days["February"] = 28; days["March"] = 31; days["April"] = 30;cout <<"March : "<<days["March"]

<<endl;}

Multimap

Sorted Associative Container Pair Associative Container Multiple Associative Container Insertion does not invalidate iterators Deletion only invalidates iterators at the

deleted element

Adapters

• Adapters are wrappers around a container

• They work with multiple containers

• They provide more specific interfaces

Stack

• LIFO• Can only insert, retrieve, and delete top element• Can use any Front Insertion or Back Insertion

Container• By default a deque is used• top() returns the top element• push() inserts at the top• pop() removes the top element

Queue

• FIFO• Elements are added to the back, and removed

from the front• Can use any Front and Back Insertion Sequence• By default a deque is used• front() retrieves element at the front• push() adds element to the back• pop() delete element at the front

Priority_queue

• Can only retrieve and delete top element

• The top element is always the largest

• Can use any RandomAccessContainer

• By default uses a vector

• top() retrieves the top element

• push() inserts an element

• pop() removes the top element

Function Objects

It is often useful to supply a function to an algorithm

It makes the algorithm more generic Sorting is the obvious example In the STL we don't actually pass a function A function object is used (which could in

fact be a function)

What is a function object?

• The simplest function object is a function pointer

• However, an object can also be used

• If operator() is overloaded the object can be used as a function

• Using an object is more flexible

• An object can maintain state

Types of function objects• Generators

Called with no arguments • Unary Functions

Called with one argument • Unary Predicate

A Unary Function that returns a boolean • Binary Functions

Called with two arguments • Binary Predicate

A Binary Function that returns a boolean

Basic Function Objects

• Strict Weak Ordering A Binary Predicate

f(x,x) is false

f(x,y) implies !f(y,x)

f(x,y) and f(y,z) implies f(x,z)

f(x,y) false and f(y,x) false implies x and y are equivalent

x and y equivalent, and y and z equivalent implies x and z equivalent

Basic Function Objects II

• Random Number Generator A Unary Function

f(N) returns an integer in the range [0,N)

Every integer in [0,N) will appear an equal number of times

• Hash Function A Unary Function

Maps an object to a size_t

Used by Hashed Associative Containers

STL Provided Function Objects

• The STL provides a large number of Function Objects

• plus, minus, multiplies, etc

• logical_or,logical_and, etc

• less, greater, etc

Find• Performs a linear search#include <iostream>#include <string>#include <list>#include <algorithm>int main() {list<string> l;l.push_back("bob");l.push_back("john");l.push_back("kate");list<string>::iterator it=

find(l.begin(),l.end(),"john");if (it==l.end())

cout << “John not found”else

cout << “John found”}

Find_if

• Finds the first element which satisfies a Predicate #include <iostream>#include <string>#include <list>#include <algorithm>#include <functional>#include <cstdlib>int main() {list<int> l;for (int i=0;i<10;i++)l.push_back(rand()%100);

cout << *find_if(l.begin(),l.end(),bind1st(less<int>(),50));

}

Adjacent_find• Performs a linear search thorugh a range of input

iterators

• Searches for two adjacent elements

• If no binary predicate is supplied finds adjacent equal elements

int main() {int array[5] = {1,2,3,4,1};const int *p = adjacent_find(array,

array+5, greater<int>());if (p!=array+5)

cout << *p << " is wrong\n";}

Find_first_of• Finds first occurance of a number of values

• Can be passed a comparison function object to use

int main() {string sentence = ”here is a string.";string vowels = "aeiou";string::iterator it =

find_first_of(sentence.begin(),sentence.end(), vowels.begin(),vowels.end());

cout << *it;}

Search

• Similar to find and find_if

• Finds subranges instead of single elements int main() {char sentence[] = "this is a sentence";char word[] = "is";char *r = search(sentence,

sentence+strlen(sentence),word, word+strlen(word));

cout << "Found " << word << " at ” << r-sentence << endl;

}

Find_end• Should be called search_end

• Same as search, except returns the last subrange that matches

int main() {char sentence[] = "this is a sentence";char word[] = "is";char *r = find_end(sentence,sentence+strlen(sentence),word, word+strlen(word));cout << "Found " << word << " at ” << r-sentence << endl;

}

Search_n

• Searches for a subsequence of n consequitive equal elements

• Can be passed a Binary Predicate with which to determine equality

int main() {int A[] = {1,1,2,3,1,1,1,2,3,1,1,1,1,2,3};int N = sizeof(A) / sizeof(A[0]);int *r = search_n(A,A+N,4,1);cout << "Sequence of 4 1's at element : ”

<< r-A << endl;}

Count

• Counts the elements that are equal to a value

• There is also a count_if which uses a Predicate int main() {

int A[] = { 1,2,3,1,2,3,1,2,3,1,2,1,1,2,3};

int N = sizeof(A)/sizeof(A[0]);

cout << "Number of 2's : ”

<< count(A,A+N,2) << endl;

}

For_each• Applies a Unary Function to each element of the range

• Returns the Unary Function struct sum {int sum;sum() : sum(0) {}void operator()(int i) { sum+=i; }

};int main() {int A[] = { 1,2,3,1,2,3,1,2,3,1,2,1,1,2,3};int N = sizeof(A) / sizeof(A[0]);sum s = for_each(A,A+N,sum());cout << s.sum << endl;

}

Equal• Compares two ranges

• Can use a BinaryPredicate for comparisons bool compare_nocase(char c1, char c2) {

return toupper(c1) == toupper(c2);

}

int main() {

const char *s1 = "a string";

const char *s2 = "A string";

if(equal(s1,s1+strlen(s1),s2,compare_nocase))

cout << "Strings are equal" << endl;

}

Mismatch• Returns the first position that two ranges differ

• Returns a pair holding two iterators

• Can use a BinaryPredicate for comparisons int main() {const char *s1 = "a string";const char *s2 = "A string";const char *s1e = s1+strlen(s1);const char *s2e = s2+strlen(s2); pair<const char*,const char*> p =

mismatch(s1,s1e,s2,compare_nocase); if (p.first == s1e && p.second == s2e)

cout << "Strings are equal" << endl;}

Lexicographic_compare• Returns true if first range is lexicographically less than

second

• Can use a BinaryPredicate for comparisons int main() {const char *s1 = "a string";const char *s2 = "A string";const int N1 = strlen(s1);const int N2 = strlen(s2);if (lexicographic_compare(s1,s1+N1,s2,

s2+N2,compare_nocase))cout << "s1 less than s2" << endl;

}

min_element and max_element• Returns an iterator at the smallest/largest element of a

range

• Can use a BinaryPredicate for comparisons int main() {list<int> l;generate_n(front_inserter(l),1000,rand);list<int>::const_iterator min =

min_element(l.begin(),l.end());list<int>::const_iterator max =

max_element(l.begin(),l.end());cout << *min << " - " << *max << endl;

}

Adapters

• Adapters transform one interface into another

• The STL provides function object adapters

• In fact it provides a lot of them

• We'll look at some of the most useful

Binder1st• Transforms a Binary Function into a Unary

Function

• It binds the first argument to a constant

• To use it a helper function bind1st is provided int main() {vector<int> v;for (int i=0;i<10;i++) v.push_back(i);vector<int>::iterator vi =

find_if(v.begin(), v.end(),bind1st(less<int>(),5));

}

Binder2nd

• Just like binder1st

• Binds the second argument to a constant int main() {vector<int> v;for (int i=0;i<10;i++) v.push_back(i);vector<int>::iterator vi =

find_if(v.begin(), v.end(),bind2nd(less<int>(),5));

}

Unary_negate and Binary_negate

• unary_negate Negates a Unary Predicate

• Easiest to use with the not1 helper function

• binary_negate Negates a Binary Predicate

• Easiest to use with the not2 helper function

Unary_compose and Binary_compose• unary_compose creates composition of two

Unary Functions

• Best to use the compose1 helper function

• compose1(f,g)(x) is the same as f(g(x))

• binary_compose creates composition of three Functions

• Best to use the compose2 helper function

• compose2(f,g1,g2)(x1,x2) is the same as f(g1(x1),g1(x2))