# Range-v3 practical examples

If you wish to learn functional programming in C++, there is no better place to start than the range-v3 library, the predecessor of C++ Ranges. This page provides functional programming solutions to commonly encountered programming problems with range-v3. You can find many more practical examples in the recently published book.

### Getting started

• Go to: https://godbolt.org/
• Choose x86-64 GCC trunk
• In the Library menu choose range-v3
• In the Output menu choose Execute the code
• Insert the following code and make sure it compiles:

### Resistors in parallel

1. Invert the values of resistance using views::transform. This will convert `[20,10,15]` to `[0.05,0.1,0.0666667]`.
2. Use accumulate to sum up the transformed resistance values.
3. Invert the result of the accumulation to arrive at `4.61538` Ω.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto v = {20,10,15};
auto r_inv = v | views::transform([](int x){return 1.0 / x;});
// [0.05,0.1,0.0666667]
auto val = 1.0 / fold_left(r_inv,0.0,std::plus{}); // 1.0 / 0.216667 = 4.61538
//auto val = 1.0 / accumulate(r_inv,0.0); // 1.0 / 0.216667 = 4.61538
std::cout << val;
}
```

### Binary to decimal conversion

1. Reverse the initial binary representation using views::reverse arriving at `[0,1,1,1]`. This will allow us to start from the power 20.
2. Generate the range `[0,1,2,3]` using views::iota.
3. Use views::transform and the shift operator to convert `[0,1,2,3]` to `[20, 21, 22, 23]`.
4. Use views::inner_product to combine the reversed binary range `[0,1,1,1]` with the powers of two `[1,2,4,8]` and sum them up.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto const v = std::vector<uint8_t> {1,1,1,0};
auto r_rev = v | views::reverse; // [0,1,1,1]
auto r_int = views::iota(0, distance(v)); // [0,1,2,3]
auto r_pow = r_int | views::transform([](int x){
return 1 << x;}); // [1,2,4,8]
auto val = inner_product(r_rev,r_pow,0); // 0*1+1*2+1*4+1*8 = 14
std::cout << val;
}
```

### snake_case to CamelCase

1. Split the string into separate words at underscore using views::split arriving at `[[f,e,e,l],[t,h,e],[f,o,r,c,e]]`.
2. Take the first letter of every word using views::take(1) and capitalize it using views::transform(...std::toupper...) , e.g. for `[f,e,e,l]` we get `[F]`.
3. Concatenate the first capitalized letter of a word with its tail using views::concat, e.g. for `[F]` and `[e,e,l]` we get `[F,e,e,l]`.
4. Flatten the range of words `[[F,e,e,l],[T,h,e],[F,o,r,c,e]]` into a single range `[F,e,e,l,T,h,e,F,o,r,c,e]` using views::join.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto const s = std::string{"feel_the_force"};
auto words = s | views::split('_'); // [[f,e,e,l],[t,h,e],[f,o,r,c,e]]
auto words_cap = words | views::transform([](auto w){
auto head = w | views::take(1)
| views::transform([](unsigned char c){return std::toupper(c);}); // e.g. [F]
return views::concat(head, w | views::tail); // e.g. [F,e,e,l]
}); // [[F,e,e,l],[T,h,e],[F,o,r,c,e]]
auto s_camelcase = words_cap | views::join | to<std::string>(); // FeelTheForce
std::cout << s_camelcase;
}
```

Would you like to get more practice?

### Fibonacci sequence

Use views::generate with a mutable lambda. The keyword mutable allows us to update the initial pair p `[0,1]` in the body of the expression in the following way:

• The first element of the new pair is the second element of the old pair, i.e. `b0`.
• The second element of the new pair is the sum of the elements of the old pair, i.e. `a0 + b0`.
Note: To generate an element of the sequence we return only a single element. Returning a pair would result in duplicates in the output.

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto rng = views::generate(
[p = std::pair{0,1}] () mutable {
auto [a0,b0] = p;
p = {b0, a0 + b0};
return a0;
});
auto fib10 = rng | views::take(10); // [0,1,1,2,3,5,8,13,21,34]
std::cout << fib10;
}
```

### Caesar cipher

Note: Single words only.

1. Generate the cyclic alphabet using views::closed_iota followed by views::cycle getting `[a,b,c...a,b,c...]`.
2. Using views::drop drop the number of characters that corresponds to the shift getting `[l,m,n...a,b,c...]`
3. Process every letter of the word "apple" using views::for_each. For example, for the letter `e` drop ('e'-'a') = (101 - 97) = 4 characters from the shifted alphabet. This means that `[l,m,n...]` becomes `[p,q,r...]`. To get the new letter `e` take the first letter of the transformed sequence, i.e. `p`.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto shift = 11;
auto const s = std::string{"apple"};

auto alphabet = views::closed_iota('a','z') | views::cycle; // [a,b,c...a,b,c...]
auto shifted_alphabet = alphabet | views::drop(shift); // [l,m,n...a,b,c...]

auto encrypted = s | views::for_each([shifted_alphabet](char letter){
return shifted_alphabet | views::drop(letter - 'a') | views::take(1);
}); // [l,a,a,w,p]
auto s_encrypted = encrypted | to<std::string>(); // laawp
std::cout << s_encrypted;
}
```

### Triangular sequence

1. Generate the range `[1,2,3...]` using views::iota.
2. Modify the generated range using views::transform(...n*(n+1)/2...) getting `[1,3,6..]`.
3. Take the desired number of elements using views::take.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto r_int = views::iota(1); // [1,2,3,4,5,6,7,8...]
auto r_triseq = r_int | views::transform([](int n){return n*(n+1)/2;}); // [1,3,6,10,15,21,28...]
auto r_first5 = r_triseq | views::take(5); // [1,3,6,10,15]
std::cout << r_first5;
}
```

Note: Range-v3 has a convenience view to accomplish the same task, namely views::partial_sum.

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto r_int = views::iota(1); // [1,2,3,4,5,6,7,8...]
auto r_triseq = r_int | views::partial_sum(std::plus{}); // [1,3,6,10,15,21,28...]
std::cout << (r_triseq | views::take(5)); // [1,3,6,10,15]
}
```

### Aronson's sequence

Aronson's sequence is a self-describing integer sequence defined by the sentence "T is the first, fourth, eleventh, sixteenth, twenty-fourth, twenty-ninth, thirty-third... letter in this sentence." Generate the sequence ignoring spaces and punctuation.

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto s = std::string{"T is the first, fourth, eleventh, sixteenth, "
"twenty-fourth, twenty-ninth, thirty-third..."};
auto r_letters_only = s | views::filter([](unsigned char c){
return std::isalpha(c);});
// [T,i,s,t,h,e,f,i,r,s,t,f,o,u,r,t,h...]
auto r_lowercase = r_letters_only | views::transform([](unsigned char c){
return std::tolower(c);});
// [t,i,s,t,h,e,f,i,r,s,t,f,o,u,r,t,h...]
auto r_pairs = views::zip(r_lowercase, views::iota(1));
// (t:1)(i:2)(s:3)(t:4)...
auto r_only_tpairs = r_pairs | views::filter([](auto && r){return r.first == 't';});
// (t:1)(t:4)...
auto r_aronson = r_only_tpairs | views::values;
// [1,4,11,16,24,29,33,35,39,45,47,51,56,58,62,64]
std::cout << r_aronson;
}
```

### Shannon's entropy

Shannon's entropy is a measure of system's disorganisation. If all the balls in the bag below are red, the probability vector would be [1,0,0,0], hence the resulting entropy would be 0. We can interpret this as that there would be no surpise in the outcome. The example below demonstrates the entropy calculation for 4 red, 2 gray, one black and one white ball. To calculate the entropy:

1. Transform the probability vector using views::transform(...-p*std::log2(p)...).
2. Sum up the elements of the transformed probability range using fold_left or accumulate.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto const v = std::vector{0.5,0.25,0.125,0.125};
auto r_p_logp =  v | views::transform([](auto p){return -p*std::log2(p);});
auto val = fold_left(r_p_logp, 0.0, std::plus{}); // 1.75
//auto val = accumulate(r_p_logp, 0.0); // 1.75
std::cout << val;
}
```

### Dot product

Note: This is a rough equivalent of ranges::inner_product.

To get the dot product:

1. Multiply the corresponding elements of the input vectors with each other using views::zip_with(std::multiplies{}...), e.g. here `[1.5*1, 2.5*2, 3.5*3]`.
2. Sum up the values of the resulting range using accumulate, e.g. here `1.5 + 5 + 10.5`.
```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

auto dotProduct = [](auto && r1, auto && r2){
return accumulate(
views::zip_with(std::multiplies{},r1,r2), 0.0);};
int main(){
auto const v1 = std::vector{1.5,2.5,3.5};
auto const v2 = std::vector{1,2,3};
std::cout << dotProduct(v1,v2); // 1.5*1 + 2.5*2 + 3.5*3 = 17
}
```

### Permutations with repetition

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto const v = std::vector{'a','b','c'};
auto rng = views::cartesian_product(v,v,v);
for(auto const & [x,y,z] : rng)
std::cout << x << ' ' << y <<  ' ' << z << '\n';
}
```
```// 27 permutations with repetition
// a a a
// a a b
// a a c
// a b a
// a b b
// ...
```

### Permutations without repetition

Note: Sort the sequence first.

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto s = std::string{"cab"};
auto s2 = s;
sort(s2);
do {
std::cout << s2 << '\n';
} while (next_permutation(s2));
}
```
```// abc
// acb
// bac
// bca
// cab
// cba
```

### Estimate area under a function

Estimate the area under the function x3 on the interval (0,2) using the midpoint Riemann sums. The figure below depicts approximately half of the area of interest.

```#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main(){
auto steps = 5;
auto a = 0.0;
auto b = 2.0;
auto dx = (b-a)/steps; // 0.4
auto f_cubic = [](double x){return x*x*x;};

auto r_int = views::iota(0, steps); // [0,1,2,3,4]
auto r_pos = r_int | views::transform([dx](int i){return dx*(0.5 + i);});
// [0.2,0.6,1,1.4,1.8]
auto r_cubic = r_pos | views::transform(f_cubic);
// [0.008, 0.216, 1, 2.744, 5.832]
auto area = dx*accumulate(r_cubic, 0.0);
// 0.4*(0.008 + 0.216 + 1 + 2.744 + 5.832) = 3.92
std::cout << area;
}
```

Tagged: Range-v3