Skip to content
Snippets Groups Projects
Commit b0b5680d authored by Marcus Mohr's avatar Marcus Mohr
Browse files

Renames STL notebooks and updates the second one.

parent f78eb541
Branches
No related tags found
No related merge requests found
File moved
%% Cell type:markdown id:bac1d62f tags:
# STL - Part 2 : More Containers & Algorithms
%% Cell type:markdown id:efdc5616 tags:
#### Sets
%% Cell type:code id:c2848a05 tags:
``` C++14
#include <iostream>
#include <set>
std::set< int > s = {1, 7, 4, 8, 2}; // create a set container and fill it
s.insert(5); // insert another element
s.insert(4);
// check how often an element is inside the set
for ( int i = 0; i < 8; ++i ) {
std::cout << i << " appears " << s.count( i ) << " times\n";
}
```
%% Cell type:markdown id:44573775 tags:
**Remarks:**
- Although we inserted **4** twice, it is only stored once in the set data structure. Just like a mathematical set a ```std::set``` can contain a specific object only once.
- Hence, ```count()``` will always return either 0 or 1.
- This shows that ```std::set``` is no sequence container.
- Instead it stores its elements in a **sorted** fashion. This guarantees logarithmic complexity for element access.
%% Cell type:markdown id:a415d236 tags:
Of course we can store various kinds of objects in a set ...
%% Cell type:code id:2213e98e tags:
``` C++14
struct myPair {
myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};
int fir_;
int sec_;
};
std::set< myPair > pairs;
```
%% Cell type:markdown id:725387bc tags:
... but there is a caveat. Check what happens, when we try to insert an object of type ```myPair``` into the set:
%% Cell type:code id:a84d9620 tags:
%% Cell type:code id:51fd5597 tags:
``` C++14
pairs.insert( myPair(1,2) );
```
%% Cell type:markdown id:2ee8f731 tags:
**What went wrong?**
As the set stores its elements in a **sorted** fashion it needs some way to **order** them.
By default that standard ```<``` relation is used.
That does not work for our ```pair``` data type.
We need to define our own comparison operator for the class.
There are plenty ways of doing that. Below we will use a free function for it.
%% Cell type:code id:e8c29594 tags:
``` C++14
%%file demo.cpp
#include <iostream>
#include <set>
struct myPair {
myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};
int fir_;
int sec_;
};
// our comparison function will return true, if the sum of the squares
// of the values in left is smaller than that in right
bool cmp( const myPair& left, const myPair& right ) {
int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;
int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;
return val1 < val2;
}
int main() {
// create two objects
myPair p1( 1, 2 );
myPair p2( 3, 4 );
// compare them
std::cout << "p1 < p2 is " << cmp( p1, p2 ) << std::endl;
std::cout << "p2 < p1 is " << cmp( p2, p1 ) << std::endl;
// a free function in C/C++ is represented by the address of its machine code in memory;
// thus, the type of argument is a function pointer;
// we need to pass that info as a second template argument;
// decltype returns the type of an expression
std::set< myPair, decltype( cmp )* > pairs ( cmp );
// alternatively we could do it ourselves; note that the (*) is important!
// but that way is not very C++-ish
// std::set< myPair, bool (*)( const myPair&, const myPair& ) > pairs( cmp );
// now insertion will work
pairs.insert( p1 );
pairs.insert( p2 );
std::cout << "insertion successful" << std::endl;
}
```
%% Output
Overwriting demo.cpp
%% Cell type:code id:0a0207dd tags:
``` C++14
!g++ demo.cpp
```
%% Cell type:code id:68e1df4b tags:
``` C++14
!./a.out
```
%% Output
p1 < p2 is 1
p2 < p1 is 0
insertion successful
%% Cell type:markdown id:f8b3109b tags:
**Note:**
The STL offers a variant **```std::unordered_set```** that stores its elements in an unsorted fashion. However, that would not solve our problem. Instead of a **comparison** we then would need to implement a **hashing function**.
%% Cell type:markdown id:59ab93ca tags:
***
The way we added the new element in the example, i.e. via
```pairs.insert( myPair(1,2) );```
again induced a copy operation. As with ```vector``` and ```list``` we could use ```emplace()``` instead.
For demonstration purposes we make the constructor of ```myPair``` verbose.
%% Cell type:code id:bdda6c33 tags:
``` C++14
%%file demo.cpp
#include <iostream>
#include <set>
struct myPair {
myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {
std::cout << "(" << fir_ << "," << sec_ << ") constructed" << std::endl;
};
int fir_;
int sec_;
};
// our comparison function: will return true, if the square of the values
// in left is smaller than that in right
bool cmp( const myPair& left, const myPair& right ) {
int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;
int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;
return val1 < val2;
}
int main() {
std::set< myPair, decltype( cmp )* > pairs( cmp );
pairs.emplace( 1, 2 );
pairs.emplace( 3, 4 );
pairs.emplace( 1, 2 );
}
```
%% Cell type:code id:8099d1a4 tags:
``` C++14
!g++ demo.cpp
```
%% Cell type:code id:5d491b27 tags:
``` C++14
!./a.out
```
%% Cell type:markdown id:8e256275 tags:
#### Associative Containers
Quoting Wikipedia:
> In computer science, an **associative array**, **map**, **symbol table**, or **dictionary** is an abstract data type composed of a collection of **(key, value) pairs**, such that each possible key appears at most once in the collection.
>
> Operations associated with this data type allow to:
>
>- add a pair to the collection;
>- remove a pair from the collection;
>- modify an existing pair;
>- lookup a value associated with a particular key.
The STL also offers associative containers. An example is ```std::map```.
%% Cell type:markdown id:a942d3bd tags:
%% Cell type:markdown id:fb3ef7bb tags:
Return to our example with the traffic light. Assume that we want to print the currect state of a specific traffic light. How to do that?
Well, using an enumeration for the state we can use the classical switch-case-construct:
%% Cell type:code id:59839aea tags:
``` C++14
%%file ampel.cpp
#include <iostream>
typedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;
void ampel( trafficLight tf ) {
switch( tf ) {
case GREEN:
std::cout << "Lights are green" << std::endl;
break;
case YELLOW:
std::cout << "Lights are yellow" << std::endl;
break;
case RED:
std::cout << "Lights are red" << std::endl;
break;
case YELLOW_RED:
std::cout << "Lights are yellow-red" << std::endl;
break;
}
}
int main() {
trafficLight tf = GREEN;
ampel( tf );
}
```
%% Cell type:code id:5847cc0d tags:
%% Cell type:code id:76c8886d tags:
``` C++14
!g++ ampel.cpp
```
%% Cell type:code id:6a1163c9 tags:
%% Cell type:code id:731e7c94 tags:
``` C++14
!./a.out
```
%% Cell type:markdown id:e5b29324 tags:
%% Cell type:markdown id:08b68b2b tags:
Another (neater) way is to associate the state with a corresponding string
%% Cell type:code id:e020ca91 tags:
%% Cell type:code id:8fe484bf tags:
``` C++14
%%file ampel.cpp
#include <iostream>
#include <map>
typedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;
int main() {
std::map< trafficLight, std::string > tf2str =
{ { GREEN, "green" }, { YELLOW, "yellow" }, { RED, "red" },
{ YELLOW_RED, "yellow-red" } };
trafficLight tf = GREEN;
std::cout << "Lights are " << tf2str[ tf ] << std::endl;
}
```
%% Cell type:code id:03890f8b tags:
%% Cell type:code id:74251bd8 tags:
``` C++14
!g++ ampel.cpp
```
%% Cell type:code id:bfd20e03 tags:
%% Cell type:code id:5b919ca3 tags:
``` C++14
!./a.out
```
%% Cell type:markdown id:2919cfda tags:
%% Cell type:markdown id:b1718cdd tags:
Another example, demonstrating additional features of ```std::map``` (converted from Gottschling, *Forschung mit modernem C++*)
%% Cell type:code id:e46017ba tags:
%% Cell type:code id:52f631d4 tags:
``` C++14
%%file constants.cpp
#include <iostream>
#include <string>
#include <map>
#include <cmath>
int main() {
// create a map and fill it with some (key,value) pairs
std::map< std::string, double > constants = { {"e", std::exp(1.0)},
{"pi", 4.0*std::atan(1.0) }, {"h", 6.6e-34} };
// subscript operator is overloaded to allow access via a key
std::cout << "Value of Planck constant is " << constants[ "h" ] << '\n';
constants[ "c" ] = 299792458;
// Hmmm, what happens here? No key "k" exists so far!
std::cout << "Value of Coulomb constant is " << constants[ "k" ] << '\n';
// find() allows to check for existance of a key; returns an iterator
// to the pair, if it is found
std::cout << "Value of pi is " << constants.find( "pi" )->second << '\n';
// if not it returns end
auto it_phi = constants.find( "phi" );
if ( it_phi != constants.end() ) {
std::cout << "The golden ratio is " << it_phi->second << '\n';
}
// can use at(), if we know the pair to exists, returns value for key
// will throw an "out_of_range" exception, if we were wrong
std::cout << "Value of Euler constant is " << constants.at( "e" ) << "\n\n";
// range-based loop
for ( auto& c: constants ) {
std::cout << "Value of " << c.first << " is " << c.second << '\n';
}
}
```
%% Cell type:code id:c6102380 tags:
%% Cell type:code id:aaba61ba tags:
``` C++14
!g++ constants.cpp
```
%% Cell type:code id:2d71c79e tags:
%% Cell type:code id:3f9362c9 tags:
``` C++14
!./a.out
```
%% Cell type:markdown id:26214da5 tags:
%% Cell type:markdown id:a7e8490c tags:
**Note:**
As with ```set``` there is also a variant of ```map``` that uses hashing instead of ordered storage and is called ```unordered_map```.
%% Cell type:markdown id:afd6c8a0 tags:
%% Cell type:markdown id:2032e7f2 tags:
#### Algorithm
The STL supports also various algorithms that work on the entries in a container. The advantage again is that these save us implementation and will (potentially) work for various kinds of containers.
We will briefly look at two examples.
%% Cell type:markdown id:fe55e848 tags:
%% Cell type:markdown id:4c239bfa tags:
**Example 1:** Remove double entries from a sequence
%% Cell type:code id:a307aade tags:
%% Cell type:code id:aed454ce tags:
``` C++14
%%file uniq.cpp
#include <algorithm>
#include <vector>
#include <iostream>
using Container = std::vector<int>;
void print( const Container& box, const std::string& note ) {
std::cout << "( ";
for( int w: box ) std::cout << w << ' ';
std::cout << ") " << note << std::endl;
}
int main() {
Container box{ 3, 5, 2, 4, 1, 2, 1 };
print( box, "<- unsorted" );
sort( box.begin(), box.end() );
print( box, "<- sorted" );
Container::iterator last = unique( begin(box), end(box) );
print( box, "<- happened in-place" );
box.resize( distance( box.begin(), last ) );
print( box, " <- truncated to new length" );
}
```
%% Cell type:code id:f2e2c4d5 tags:
%% Cell type:code id:57c92377 tags:
``` C++14
!g++ uniq.cpp
```
%% Cell type:code id:b90a9b8a tags:
%% Cell type:code id:b190c1c6 tags:
``` C++14
!./a.out
```
%% Cell type:markdown id:f52ee84c tags:
%% Cell type:markdown id:e9140c15 tags:
**Note:**
Replacing ```vector``` by ```list``` via
using Container = std::vector<int>;
will not work in this example. ```std::sort()``` is implemented to work with a **random access iterator**, which list does not provide. Instead it has its own ```list::sort()```
and ```list::unique()``` member functions.
%% Cell type:markdown id:e435d1ec tags:
%% Cell type:markdown id:7856aa32 tags:
***
**Example 2:** Performing reductions on an array
The STL also provides some algorithms for performing numeric operations. For these we need to include ```<numeric>```.
%% Cell type:code id:ecfa4c0a tags:
%% Cell type:code id:bed09ecd tags:
``` C++14
%%file accumulate.cpp
#include <numeric>
#include <vector>
#include <iostream>
int alternate( int a, int b ) {
static bool odd = false; // static preserves value of local variable
if( odd ) {
odd = false;
return a-b;
}
else {
odd = true;
return a+b;
}
}
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
int gs = std::accumulate( v.begin(), v.end(), 0 );
std::cout << "sum is " << gs << std::endl;
int check = std::accumulate( v.begin(), v.end(), -15 );
if( check == 0 ) std::cout << "checks out" << std::endl;
int prod = std::accumulate( v.begin(), v.end(), 1, std::multiplies<int>() );
std::cout << "5! = " << prod << std::endl;
int alt = std::accumulate( v.begin(), v.end(), 0, alternate );
std::cout << "Alternating sum gives " << alt << std::endl;
// 0 + 1 - 2 + 3 - 4 + 5
}
```
%% Cell type:code id:c9c3cb86 tags:
%% Cell type:code id:16a6d2e8 tags:
``` C++14
!g++ accumulate.cpp
```
%% Cell type:code id:499d6af6 tags:
%% Cell type:code id:015eb80f tags:
``` C++14
!./a.out
```
%% Cell type:code id:dc2715fd tags:
%% Cell type:code id:1efec163 tags:
``` C++14
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment