Как стать автором
Обновить
890.81
OTUS
Цифровые навыки от ведущих экспертов

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

Время на прочтение4 мин
Количество просмотров36K
Автор оригинала: Marius Bancila
Снова здравствуйте. Перевод следующего материала подготовлен специально для студентов курса «Разработчик 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(' '));


Была ли статья полезной для вас? Пишите в комментарии.
Теги:
Хабы:
Всего голосов 52: ↑47 и ↓5+42
Комментарии36

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS