[Перевод] Примеры C++ кода до и после Ranges

15 ИЮНЯ 2022

------------------------------------------------------------------------

Наш телеграм-канал: t.me/@mailsgun_online

Youtube: youtube.com/@mailsgun_online

------------------------------------------------------------------------

OTUS. Онлайн-образование
Авторские онлайн‑курсы для профессионалов

  • Перевод
Снова здравствуйте. Перевод следующего материала подготовлен специально для студентов курса «Разработчик C++», занятия по которому стартуют уже 27 июня.

Библиотека Ranges была принята в C++20 на совещании стандартного комитета в Сан-Диего в ноябре прошлого года. Библиотека предоставляет компоненты для обработки диапазонов значений, направленных на упрощение нашего кода. К сожалению, библиотека Ranges не очень хорошо документирована, из-за этого ее труднее понять тем, кто хотел бы ее освоить. Этот пост предназначен для ознакомления с примерами кода, написанного с использованием Ranges и без нее.

Реализация библиотеки Ranges Эрика Ниблера доступна здесь. Она работает с Clang 3.6.2 или новее, gcc 5.2 или новее, и VC ++ 15.9 или новее. Примеры кода ниже были написаны и протестированы с последними версиями компиляторов. Стоит отметить, что эти примеры представляют собой типичные реализации и не обязательно являются единственными решениями, которые можно придумать.

Хотя стандартным пространством имен для библиотеки Ranges является std::ranges, в данной текущей реализации библиотеки оно ranges::v3.

Следующие псевдонимы пространства имен используются в примерах ниже:

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

Также, для упрощения, мы будем ссылаться на следующие объекты, функции и лямбды:

std::string to_roman(int value)
{
   std::vector<std::pair<int, char const*>> 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<int> 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; };

АПДЕЙТ: Я хотел бы поблагодарить Эрика Ниблера и всех остальных, кто комментировал ниже, с предложениями для этих примеров кода. Я обновил несколько на основе их отзывов.

Вывести все элементы диапазона:

До Ranges После Ranges
С++ С++
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);

Выведите все элементы диапазона в обратном порядке:

До Ranges После Ranges
С++ С++
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);
};

Выведите только четные элементы диапазона, но в обратном порядке:

До Ranges После Ranges
С++ С++
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);
};

Пропустите первые два элемента диапазона и выведите только четные из следующих трех:

До Ranges После Ranges
С++ С++

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);
};

Выведите числа от 101 до 200:

До Ranges После Ranges
С++ С++
for (int n = 101; n <= 200; ++n)
{
   print_elem(n);
}

for (auto n : rs::iota_view(101, 201))
{
   print_elem(n);
}

Выведите все римские цифры от 101 до 200. Чтобы преобразовать число в соответствующее римское число, используется функция to_roman(), показанная выше.

До Ranges После Ranges
С++ С++

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);

Выведите римские цифры последних трех чисел, делимых на 7 в диапазоне [101, 200], в обратном порядке.

До Ranges После Ranges
С++ С++

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);
}

Создайте диапазон строк, содержащий римские цифры последних трех чисел, кратных 7 в диапазоне [101, 200], в обратном порядке.

До Ranges После Ranges
С++ С++

std::vector<std::string> 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;

Измените несортированный диапазон, чтобы он сохранял только уникальные значения, но в обратном порядке.

До Ranges После Ranges
С++ С++

std::vector<int> 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<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 };
v = std::move(v)
    ra::sort
    ra::unique
    ra::reverse;

Удалите два наименьших и два самых больших значения диапазона и оставьте остальные, упорядоченные во втором диапазоне.

До Ranges После Ranges
С++ С++

std::vector<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 };
std::vector<int> 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<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 };
auto v2 = v
          rs::copy
          ra::sort
          ra::slice(2, rs::end - 2);

Объединить все строки в данном диапазоне в одно значение.

До Ranges После Ranges
С++ С++

std::vector<std::string> words {
   "Lorem", " ", "ipsum", " ",
   "dolor", " ", "sit", " ",
   "amet"};
std::string text;
for (auto const & word : words)
   text += word;

std::vector<std::string> words {
   "Lorem", " ", "ipsum", " ",
   "dolor", " ", "sit", " ",
   "amet"};
std::string text = words
               	rs::move
               	ra::join;

Подсчитайте количество слов (разделенных пробелом) в тексте.

До Ranges После Ranges
С++ С++

auto text = "Lorem ipsum dolor sit amet";
std::istringstream iss(text);
std::vector<std::string> words(
   std::istream_iterator<std::string>{iss},
   std::istream_iterator<std::string>());
auto count = words.size();
// or
size_t count = 0;
std::vector<std::string> 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(' '));

Была ли статья полезной для вас? Пишите в комментарии.

Теги:

Похожие публикации

Source: habr1

Ну и напоследок напоминаю вам о нашем новом проекте vkserfing bot.