C++¶
A brief introduction to features of C++ that are not found in C, using
C++11 features where possible. We will assume the following
#includes
in the code snippets. As usual, we will exclude classes
and any discussion of object-oriented programming.
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iterator>
#include "/usr/local/include/armadillo"
Hello, world¶
Note the use of the iostream
library and the standard namepace
qualification std::cout
.
int main()
{
std::cout << "Hello, world!\n";
}
Output¶
Hello, world!
Namespaces¶
Just like Python, C++ has namespaces that allow us to build large
libraries without worrying about name collisions. In the Hello world
program, we used the explicit name std::cout
indicating that
cout
is a member of the standard workspace. We can also use the
using
keyword to import selected functions or classes from a
namespace.
using std::cout;
int main()
{
cout << "Hello, world!\n";
}
For small programs, we sometimes import the entire namespace for convenience, but this may cause namespace collisions in larger programs.
using namespace std;
int main()
{
cout << "Hello, world!\n";
}
You can easily create your own namespace.
namespace sta_663 {
const double pi=2.14159;
void greet(string name) {
cout << "\nTraditional first program\n";
cout << "Hello, " << name << "\n";
}
}
int main()
{
cout << "\nUsing namespaces\n";
string name = "Tom";
cout << sta_663::pi << "\n";
sta_663::greet(name);
}
Output¶
Using namespaces
2.14159
Traditional first program
Hello, Tom
Looping¶
Note the traditional for loop and the new range for loop. There is also
a while
loop (not shown).
// for loops
int main()
{
int x[] = {1, 2, 3, 4, 5};
cout << "\nTraditional for loop\n";
for (int i=0; i < sizeof(x)/sizeof(x[0]); i++) {
cout << i << endl;
}
cout << "\nRanged for loop\n\n";
for (auto &i : x) {
cout << i << endl;
}
}
Output¶
Traditional for loop
0
1
2
3
4
Ranged for loop
1
2
3
4
5
Exercise 1
Use loop to generate the 12 by 12 times table. Compile and run. You don’t have to worry much about formatting, but the output should have 12 rows with numbers separated by spaces.
In [ ]:
Functions and Lambdas¶
// simple function
int add0(int a, int b) {
return a + b;
}
// simple function with reference variables
void add1(int a, int b, int& c) {
c = a + b;
}
// When to use values and when to use refernces in function arguments?
// Use values when the variable is small (e.g. a double)
double f1(double x) { return 2 * x; };
// Use const references when the variable is large to avoid copying
double f2(const vector<dohble>& v) {
double s = 0.0;
for (auto x: v) {
s += x;
}
return s;
}
// Use references when you want to change the variable within the function
void f3(vector<double>& v) {
for (auto& x: v) {
x++;
}
}
// lambda function
auto add2 = [] (int a, int b) { return a + b; };
int main() {
cout << "\nStandard function\n";
int a = 3, b = 4;
cout << add0(a, b) << endl;
int c = 0;
cout << "\nStandard with reference varaibles\n";
add1(a, b, c);
cout << c << endl;
cout << "\nLambda function\n";
cout << add2(a, b) << endl;
auto add3 = [c] (int a, int b) { return c * add2(a, b); };
c -= 5;
cout << "\nLambda function with value capture\n";
cout << add3(a, b) << endl;
auto add4 = [&c] (int a, int b) { return c * add2(a, b); };
cout << "\nLambda function with reference capture\n";
cout << add4(a, b) << endl;
}
Output¶
Standard function
7
Standard with reference varaibles
7
Lambda function
7
Lambda function with value capture
49
Lambda function with reference capture
14
Templates¶
// templates
template <typename T>
T add5(T a, T b) { return a + b; }
int main()
{
cout << "\nTemplate function with ints\n";
cout << add5(3, 4) << endl;
cout << "\nTemplate function with doubles\n";
cout << add5(3.14, 2.78) << endl;
}
Output¶
Template function with ints
7
Template function with doubles
5.92
Iterators¶
int main()
{
int x[] = {1, 2, 3, 4, 5};
cout << "\nUsing iterators\n";
for (auto it=begin(x); it != end(x); it++) {
cout << *it << endl;
}
}
Output¶
Using iterators
1
2
3
4
5
Containers¶
int main()
{
vector<double> v = {1,2,3};
cout << "\nUsing the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
v.push_back(4);
v.push_back(5);
cout << "\nGrowing the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
v.pop_back();
cout << "\nShrinking the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
cout << "\nUsing the unordered_map container\n";
unordered_map<string, int> dict = { {"ann", 23}, {"bob", 32}, {"charles", 17}};
dict["doug"] = 30;
for (auto it=begin(dict); it != end(dict); it++) {
cout << it->first << ", " << it->second << endl;
}
cout << dict["bob"] << endl;
}
Output¶
Using the vector container
1
2
3
Growing the vector container
1
2
3
4
5
Shrinking the vector container
1
2
3
4
Using the unordered_map container
doug, 30
charles, 17
bob, 32
ann, 23
32
Exercise 2
Write a function that takes a vector of doubles returns the squared vector. Compile and run the function with the initial vector containing 1.0, 2.0, 3.0, 4.0, 5.0.
In [ ]:
EXercise 3
Convert the function from Exercise 2 so that it works for lists or vectors of ints, floats and doubles.
In [ ]:
Algorithms¶
int main()
{
void show_algorithms() {
vector<int> v(10, 0);
cout << "\nWorking with standard library algorithm\n";
cout << "\nInitial state\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << " ";
}
cout << endl;
cout << "\nAfter iota\n";
iota(begin(v), end(v), 5);
for (auto it=begin(v); it != end(v); it++) {
cout << *it << " ";
}
cout << endl;
cout << "\nSimple accumulate\n";
int s = accumulate(begin(v), end(v), 0);
cout << s << endl;
cout << "\nAccumulate with custom sum of squares reduction\n";
int t = accumulate(begin(v), end(v), 0, [] (int acc, int x) { return acc + x*x; });
cout << t << endl;
}
Output¶
Working with standard library algorithm
Initial state
0 0 0 0 0 0 0 0 0 0
After iota
5 6 7 8 9 10 11 12 13 14
Simple accumulate
95
Accumulate with custom sum of squares reduction
985
Exercise 4
Write a function to calculate the mean of a vector of numbers using
accumulate
from the <numeric>
library. Compile and test with
some vectors.
In [ ]:
Function pointers¶
int main()
{
cout << "\nUsing generalized function pointers\n";
using func = function<double(double, double)>;
auto f1 = [](double x, double y) { return x + y; };
auto f2 = [](double x, double y) { return x * y; };
auto f3 = [](double x, double y) { return x + y*y; };
double x = 3, y = 4;
vector<func> funcs = {f1, f2, f3,};
for (auto& f : funcs) {
cout << f(x, y) << "\n";
}
}
Output¶
Using generalized function pointers
7
12
19
Exercise 5
Implement Newton’s method in 1D for root finding. Pass in the function and gradient as generalized function pointers. Test your function with some simple polynomial and starting points.
In [ ]:
Random numbers¶
C++ now comes with its own collection of random number generators and quite a broad selection of distributions. See here for a great explanation.
int main()
{
cout << "\nGenerating random numbers\n";
// start random number engine wiht fixed seed
default_random_engine re{12345};
uniform_int_distribution<int> uniform(1,6); // lower and upper bounds
poisson_distribution<int> poisson(30); // rate
student_t_distribution<double> t(10); // degrees of freedom
auto runif = bind (uniform, re);
auto rpois = bind(poisson, re);
auto rt = bind(t, re);
for (int i=0; i<10; i++) {
cout << runif() << ", " << rpois() << ", " << rt() << "\n";
}
}
Output¶
Generating random numbers
3, 30, 0.0796641
5, 38, 0.163947
3, 26, -0.570003
6, 27, 0.872475
6, 33, -0.260322
2, 28, 0.798292
1, 22, 0.00164128
4, 29, 0.633913
5, 41, 1.00468
6, 31, -0.00420647
Exercise 6
Generate 1000 random points from the exponential distribution and save
as a comma-separated values (CSV) file. Open the file in Python and plot
the distribution using plt.hist
.
In [ ]:
Numeric library¶
Armadillo is an accessible library for doing numeric operations, much
like numpy
in Python. Please see official
documentation for details. It
provides vectors, matrices, tensors, linear algebra, statistical
functions and a limited set of convenient random number generators.
int main()
{
using namespace arma;
vec u = linspace<vec>(0,1,5);
vec v = ones<vec>(5);
mat A = randu<mat>(4,5); // uniform random deviates
mat B = randn<mat>(4,5); // normal random deviates
cout << "\nVecotrs in Armadillo\n";
cout << u << endl;
cout << v << endl;
cout << u.t() * v << endl;
cout << "\nRandom matrices in Armadillo\n";
cout << A << endl;
cout << B << endl;
cout << A * B.t() << endl;
cout << A * v << endl;
cout << "\nQR in Armadillo\n";
mat Q, R;
qr(Q, R, A.t() * A);
cout << Q << endl;
cout << R << endl;
}
Output¶
Vecotrs in Armadillo
0
0.2500
0.5000
0.7500
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
2.5000
Random matrices in Armadillo
7.8264e-06 5.3277e-01 6.7930e-01 8.3097e-01 6.7115e-01
1.3154e-01 2.1896e-01 9.3469e-01 3.4572e-02 7.6982e-03
7.5561e-01 4.7045e-02 3.8350e-01 5.3462e-02 3.8342e-01
4.5865e-01 6.7886e-01 5.1942e-01 5.2970e-01 6.6842e-02
-0.7649 1.2041 -0.7020 1.1862 0.8284
1.7313 0.0937 1.6814 -0.8631 0.9426
0.1454 -0.6920 0.2742 0.6810 0.2091
0.7032 0.2610 0.1752 1.3165 -0.5897
1.7063 1.1075 0.5238 0.9563
-0.4457 1.7973 0.1490 0.3544
-0.4095 2.2726 0.2990 0.4551
0.7857 1.3368 0.1140 1.2487
2.7142
1.3275
1.6230
2.2535
QR in Armadillo
-0.6776 0.6377 0.3253 0.0944 -0.1395
-0.3188 -0.4409 0.3521 0.4177 0.6368
-0.5524 -0.2366 -0.7774 0.1504 -0.1092
-0.2443 -0.5816 0.4071 -0.1709 -0.6380
-0.2727 -0.0688 -0.0099 -0.8745 0.3950
-1.1785e+00 -1.3394e+00 -2.1015e+00 -1.3527e+00 -1.0229e+00
0e+00 -8.3421e-01 -9.7607e-01 -9.9515e-01 -5.3245e-01
0e+00 0e+00 -4.6336e-01 7.6793e-02 -4.0376e-03
0e+00 0e+00 0e+00 -2.0273e-01 -3.2745e-01
0e+00 0e+00 0e+00 0e+00 1.1102e-16
Exercise 7
Use the armadillo library to
- Generate 10 x-coordinates linearly spaced between 10 and 15
- Generate 10 random y-values as \(y = 3x^2 - 7x + 2 + \epsilon\) where \(\epsilon \sim N(0,1)\)
- Find the length of \(x\) and \(y\) and the Euclidean distance between \(x\) and \(y\)
- Find the correlation between \(x\) and \(y\)
- Solve the linear system to find a quadratic fit for this data
In [ ]:
Collected source code¶
In [2]:
%%file main.cpp
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <functional>
#include <random>
#include <armadillo>
using namespace std;
/* Topics
*
* - for loop
*
*
* - functions
* - lambdas
* - templates
*
* - iterators
* - containers
* - algorithms
* - Armadillo
*/
// hello world
void show_hello() {
cout << "Hello, world!\n";
}
namespace sta_663 {
const double pi=2.14159;
void greet(string name) {
cout << "\nTraditional first program\n";
cout << "Hello, " << name << "\n";
}
}
void show_namespace() {
cout << "\nUsing namespaces\n";
string name = "Tom";
cout << sta_663::pi << "\n";
sta_663::greet(name);
}
// for loops
void show_for() {
int x[] = {1, 2, 3, 4, 5};
cout << "\nTraditional for loop\n";
for (int i=0; i < sizeof(x)/sizeof(x[0]); i++) {
cout << i << endl;
}
cout << "\nRanged for loop\n\n";
for (auto &i : x) {
cout << i << endl;
}
}
// simple funciton
int add0(int a, int b) {
return a + b;
}
// simple function with reference variables
void add1(int a, int b, int& c) {
c = a + b;
}
// lambda function
auto add2 = [] (int a, int b) { return a + b; };
void show_func() {
cout << "\nStandard function\n";
int a = 3, b = 4;
cout << add0(a, b) << endl;
int c = 0;
cout << "\nStandard with reference varaibles\n";
add1(a, b, c);
cout << c << endl;
cout << "\nLambda function\n";
cout << add2(a, b) << endl;
auto add3 = [c] (int a, int b) { return c * add2(a, b); };
c -= 5;
cout << "\nLambda function with value capture\n";
cout << add3(a, b) << endl;
auto add4 = [&c] (int a, int b) { return c * add2(a, b); };
cout << "\nLambda function with reference capture\n";
cout << add4(a, b) << endl;
}
// templates
template <typename T>
T add5(T a, T b) { return a + b; }
void show_template() {
cout << "\nTemplate function with ints\n";
cout << add5(3, 4) << endl;
cout << "\nTemplate function with doubles\n";
cout << add5(3.14, 2.78) << endl;
}
void show_iterators() {
int x[] = {1, 2, 3, 4, 5};
cout << "\nUsing iterators\n";
for (auto it=begin(x); it != end(x); it++) {
cout << *it << endl;
}
}
void show_containers() {
vector<double> v = {1,2,3};
cout << "\nUsing the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
v.push_back(4);
v.push_back(5);
cout << "\nGrowing the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
v.pop_back();
cout << "\nShrinking the vector container\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << endl;
}
cout << "\nUsing the unordered_map container\n";
unordered_map<string, int> dict = { {"ann", 23}, {"bob", 32}, {"charles", 17}};
dict["doug"] = 30;
for (auto it=begin(dict); it != end(dict); it++) {
cout << it->first << ", " << it->second << endl;
}
cout << dict["bob"] << endl;
}
void show_algorithms() {
vector<int> v(10, 0);
cout << "\nWorking with standard library algorithm\n";
cout << "\nInitial state\n";
for (auto it=begin(v); it != end(v); it++) {
cout << *it << " ";
}
cout << endl;
cout << "\nAfter iota\n";
iota(begin(v), end(v), 5);
for (auto it=begin(v); it != end(v); it++) {
cout << *it << " ";
}
cout << endl;
cout << "\nSimple accumulate\n";
int s = accumulate(begin(v), end(v), 0);
cout << s << endl;
cout << "\nAccumulate with custom sum of squares reduction\n";
int t = accumulate(begin(v), end(v), 0, [] (int acc, int x) { return acc + x*x; });
cout << t << endl;
}
void show_functional() {
cout << "\nUsing generalized function pointers\n";
using func = function<double(double, double)>;
auto f1 = [](double x, double y) { return x + y; };
auto f2 = [](double x, double y) { return x * y; };
auto f3 = [](double x, double y) { return x + y*y; };
double x = 3, y = 4;
vector<func> funcs = {f1, f2, f3,};
for (auto& f : funcs) {
cout << f(x, y) << "\n";
}
}
void show_random() {
cout << "\nGenerating random numbers\n";
// start random number engine wiht fixed seed
default_random_engine re{12345};
uniform_int_distribution<int> uniform(1,6); // lower and upper bounds
poisson_distribution<int> poisson(30); // rate
student_t_distribution<double> t(10); // degrees of freedom
auto runif = bind (uniform, re);
auto rpois = bind(poisson, re);
auto rt = bind(t, re);
for (int i=0; i<10; i++) {
cout << runif() << ", " << rpois() << ", " << rt() << "\n";
}
}
void show_amrmadillo() {
using namespace arma;
vec u = linspace<vec>(0,1,5);
vec v = ones<vec>(5);
mat A = randu<mat>(4,5);
mat B = randn<mat>(4,5);
cout << "\nVecotrs in Armadillo\n";
cout << u << endl;
cout << v << endl;
cout << u.t() * v << endl;
cout << "\nRandom matrices in Armadillo\n";
cout << A << endl;
cout << B << endl;
cout << A * B.t() << endl;
cout << A * v << endl;
cout << "\nQR in Armadillo\n";
mat Q, R;
qr(Q, R, A.t() * A);
cout << Q << endl;
cout << R << endl;
}
int main() {
show_hello();
show_namespace();
show_for();
show_func();
show_template();
show_iterators();
show_containers();
show_algorithms();
show_functional();
show_random();
show_amrmadillo();
}
Overwriting main.cpp
In [5]:
%%bash
g++ -O3 -std=c++11 -larmadillo main.cpp -o main
In [6]:
%%bash
./main
Hello, world!
Using namespaces
2.14159
Traditional first program
Hello, Tom
Traditional for loop
0
1
2
3
4
Ranged for loop
1
2
3
4
5
Standard function
7
Standard with reference varaibles
7
Lambda function
7
Lambda function with value capture
49
Lambda function with reference capture
14
Template function with ints
7
Template function with doubles
5.92
Using iterators
1
2
3
4
5
Using the vector container
1
2
3
Growing the vector container
1
2
3
4
5
Shrinking the vector container
1
2
3
4
Using the unordered_map container
doug, 30
charles, 17
bob, 32
ann, 23
32
Working with standard library algorithm
Initial state
0 0 0 0 0 0 0 0 0 0
After iota
5 6 7 8 9 10 11 12 13 14
Simple accumulate
95
Accumulate with custom sum of squares reduction
985
Using generalized function pointers
7
12
19
Generating random numbers
3, 30, 0.0796641
5, 38, 0.163947
3, 26, -0.570003
6, 27, 0.872475
6, 33, -0.260322
2, 28, 0.798292
1, 22, 0.00164128
4, 29, 0.633913
5, 41, 1.00468
6, 31, -0.00420647
Vecotrs in Armadillo
0
0.2500
0.5000
0.7500
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
2.5000
Random matrices in Armadillo
7.8264e-06 5.3277e-01 6.7930e-01 8.3097e-01 6.7115e-01
1.3154e-01 2.1896e-01 9.3469e-01 3.4572e-02 7.6982e-03
7.5561e-01 4.7045e-02 3.8350e-01 5.3462e-02 3.8342e-01
4.5865e-01 6.7886e-01 5.1942e-01 5.2970e-01 6.6842e-02
-0.7649 1.2041 -0.7020 1.1862 0.8284
1.7313 0.0937 1.6814 -0.8631 0.9426
0.1454 -0.6920 0.2742 0.6810 0.2091
0.7032 0.2610 0.1752 1.3165 -0.5897
1.7063 1.1075 0.5238 0.9563
-0.4457 1.7973 0.1490 0.3544
-0.4095 2.2726 0.2990 0.4551
0.7857 1.3368 0.1140 1.2487
2.7142
1.3275
1.6230
2.2535
QR in Armadillo
-0.6776 0.6377 0.3253 0.0944 -0.1395
-0.3188 -0.4409 0.3521 0.4177 0.6368
-0.5524 -0.2366 -0.7774 0.1504 -0.1092
-0.2443 -0.5816 0.4071 -0.1709 -0.6380
-0.2727 -0.0688 -0.0099 -0.8745 0.3950
-1.1785e+00 -1.3394e+00 -2.1015e+00 -1.3527e+00 -1.0229e+00
0e+00 -8.3421e-01 -9.7607e-01 -9.9515e-01 -5.3245e-01
0e+00 0e+00 -4.6336e-01 7.6793e-02 -4.0376e-03
0e+00 0e+00 0e+00 -2.0273e-01 -3.2745e-01
0e+00 0e+00 0e+00 0e+00 1.1102e-16
In [ ]: