C ++ code examples before and after Ranges

Original author: Marius Bancila
  • Transfer
Hello again. The translation of the following material was prepared specifically for students of the course "C ++ Developer" , classes on which will start on June 27.



The Ranges library was adopted in C ++ 20 at a meeting of the standard committee in San Diego last November. The library provides components for processing ranges of values ​​designed to simplify our code. Unfortunately, the Ranges library is not very well documented, which makes it harder to understand for those who would like to master it. This post is intended to provide examples of code written using and without Ranges.

Eric Niebler's Ranges library implementation is available here.. It works with Clang 3.6.2 or later, gcc 5.2 or later, and VC ++ 15.9 or later. The code examples below have been written and tested with the latest versions of compilers. It is worth noting that these examples are typical implementations and are not necessarily the only solutions that you can come up with.

Although the standard namespace for the Ranges library is std::ranges, in this current library implementation it is ranges::v3.

The following namespace aliases are used in the examples below:

namespace rs = ranges::v3;
namespace rv = ranges::v3::view;
namespace ra = ranges::v3::action;

Also, for simplification, we will refer to the following objects, functions and lambdas:

std::string to_roman(int value)
{
   std::vector> roman
   {
      { 1000, "M" },{ 900, "CM" },
      { 500, "D" },{ 400, "CD" },
      { 100, "C" },{ 90, "XC" },
      { 50, "L" },{ 40, "XL" },
      { 10, "X" },{ 9, "IX" },
      { 5, "V" },{ 4, "IV" },
      { 1, "I" }
   };
   std::string result;
   for (auto const & [d, r]: roman)
   {
      while (value >= d)
      {
     	result += r;
     	value -= d;
      }
   }
   return result;
}
std::vector v{1,1,2,3,5,8,13,21,34};
auto print_elem = [](auto const e) {std::cout << e << '\n'; };
auto is_even = [](auto const i) {return i % 2 == 0; };

APDATE : I would like to thank Eric Nibler and everyone else who commented below, with suggestions for these code examples. I updated a few based on their reviews.

Print all the elements of the range:

To the rangesAfter rangs
C ++C ++
std::for_each(
   std::cbegin(v), std::cend(v),
   print_elem);
// or
for(auto const i : v)
{
   print_elem(i);
};
rs::for_each(
   std::cbegin(v), std::cend(v),
   print_elem);
// or
rs::for_each(std::as_const(v), print_elem);


Print all the elements of the range in the reverse order:

To the rangesAfter rangs
C ++C ++
std::for_each(
   std::crbegin(v), std::crend(v),
   print_elem);
 
rs::for_each(
   std::crbegin(v), std::crend(v),
   print_elem);
// or
for (auto const i : v | rv::reverse)
{
   print_elem(i);
};


Print only the even elements of the range, but in the reverse order:

To the rangesAfter rangs
C ++C ++
std::for_each(
   std::crbegin(v), std::crend(v),
   [print_elem](auto const i) {
      if(i % 2 == 0)
     	print_elem(i);
   });
 
for (auto const i : v
                  | rv::reverse
                  | rv::filter(is_even))
{
   print_elem(i);
};


Skip the first two elements of the range and print only even ones from the following three:

To the rangesAfter rangs
C ++C ++
 
auto it = std::cbegin(v);
std::advance(it, 2);
auto ix = 0;
while (it != std::cend(v) && ix++ < 3)
{
   if (is_even(*it))
      print_elem(*it);
   it++;
}
 
for (auto const i : v
                  | rv::drop(2)
                  | rv::take(3)
                  | rv::filter(is_even))
{
   print_elem(i);
};


Print numbers from 101 to 200:

To the rangesAfter rangs
C ++C ++
for (int n = 101; n <= 200; ++n)
{
   print_elem(n);
}
 
for (auto n : rs::iota_view(101, 201))
{
   print_elem(n);
}


Print all Roman numerals from 101 to 200. To convert a number to the corresponding Roman number, use the function to_roman()shown above.

To the rangesAfter rangs
C ++C ++
 
for (int i = 101; i <= 200; ++i)
{
   print_elem(to_roman(i));
}
 
for (auto n : rs::iota_view(101, 201)
            | rv::transform(to_roman))
{
   print_elem(n);
}
// or
rs::for_each(rv::iota(101, 201),
         	print_element, to_roman);


Print the Roman numerals of the last three numbers divisible by 7 in the range [101, 200], in reverse order.

To the rangesAfter rangs
C ++C ++
 
for (int n = 200, count=0; n >= 101 && count < 3; --n)
{
   if (n % 7 == 0)
   {
      print_elem(to_roman(n));
      count++;
   }
}
 
for (auto n : rs::iota_view(101, 201)
            | rv::reverse
            | rv::filter([](auto v) {
                return v % 7 == 0; })
            | rv::transform(to_roman)
            | rv::take(3))
{
   print_elem(n);
}


Create a range of strings containing the roman numerals of the last three numbers that are multiples of 7 in the range [101, 200], in reverse order.

To the rangesAfter rangs
C ++C ++
 
std::vector v;
for (int n = 200, count = 0;
 	n >= 101 && count < 3; --n)
{
   if (n % 7 == 0)
   {
      v.push_back(to_roman(n));
      count++;
   }
}
auto v = rs::iota_view(101, 201)
   	| rv::reverse
   	| rv::filter([](auto v) {return v % 7 == 0; })
   	| rv::transform(to_roman)
   	| rv::take(3)
   	| rs::to_vector;


Change the unsorted range so that it retains only unique values, but in the reverse order.

To the rangesAfter rangs
C ++C ++
 
std::vector v{ 21, 1, 3, 8, 13, 1, 5, 2 };
std::sort(std::begin(v), std::end(v));
v.erase(
   std::unique(std::begin(v), std::end(v)),
   std::end(v));
std::reverse(std::begin(v), std::end(v));
 
std::vector v{ 21, 1, 3, 8, 13, 1, 5, 2 };
v = std::move(v) |
    ra::sort |
    ra::unique |
    ra::reverse;


Delete the two smallest and the two largest values ​​of the range and leave the rest ordered in the second range.

To the rangesAfter rangs
C ++C ++
 
std::vector v{ 21, 1, 3, 8, 13, 1, 5, 2 };
std::vector v2 = v;
std::sort(std::begin(v2), std::end(v2));
auto first = std::begin(v2);
std::advance(first, 2);
auto last = first;
std::advance(last, std::size(v2) - 4);
v2.erase(last, std::end(v2));
v2.erase(std::begin(v2), first);
 
std::vector v{ 21, 1, 3, 8, 13, 1, 5, 2 };
auto v2 = v |
          rs::copy |
          ra::sort |
          ra::slice(2, rs::end - 2);


Combine all rows in a given range into a single value.

To the rangesAfter rangs
C ++C ++
 
std::vector words {
   "Lorem", " ", "ipsum", " ",
   "dolor", " ", "sit", " ",
   "amet"};
std::string text;
for (auto const & word : words)
   text += word;
 
std::vector words {
   "Lorem", " ", "ipsum", " ",
   "dolor", " ", "sit", " ",
   "amet"};
std::string text = words |
               	rs::move |
               	ra::join;


Count the number of words (separated by a space) in the text.

To the rangesAfter rangs
C ++C ++

auto text = "Lorem ipsum dolor sit amet";
std::istringstream iss(text);
std::vector words(
   std::istream_iterator{iss},
   std::istream_iterator());
auto count = words.size();
// or
size_t count = 0;
std::vector words;
std::string token;
std::istringstream tokenStream(text);
while (std::getline(tokenStream, token, ' '))
{
   ++count;
}

auto text = "Lorem ipsum dolor sit amet";
auto count = rs::distance(
   rv::c_str(text) | rv::split(' '));


Was the article helpful to you? Write in the comments.

Also popular now: