cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
88 stars 0 forks source link

`chain`, `zip` для итераторов и диапазонов #455

Closed GeorgiiFirsov closed 2 years ago

GeorgiiFirsov commented 3 years ago

Суть

В целом сама идея, думаю, и из заголовка ясна. Предлагаю добавить такие функции, как zip и chain.

Первая возвращает адаптер, итератор которого содержит кортеж из ссылок/значений (этот момент вообще требуется дополнительно обдумать, как такое лучше реализовать, возможно в комментах напишу продолжение), полученных из соответствующих переданным контейнерам/диапазонам итераторов. При этом если один диапазон короче другого, то следует итерироваться до конца самого короткого. Хотя в целом тут можно добавить вариант с проверкой равенства длин диапазонов.

Вторая же "ставит" один диапазон за другим, позволяя по окончании первого перейти к элементам второго. Внутренние типы диапазонов должны совпадать или хотя бы быть приводимыми друг к другу или к общему родителю (если ссылки или указатели).

Стоит отметить, что ограничиваться двумя аргументами не представляется рациональным - данные концепции нетрудно обобщаются на произвольное количество диапазонов.

Примеры

Думаю, примеров можно подобрать много и во многих областях, когда требуется проитерироваться по нескольким последовательностям примерно следующим образом (концепт):

std::vector<int> SomeValues = ...;
std::vector<std::string> SomeStrings = ...;

for (const auto& Pair : zip(SomeValues, SomeStrings)) {
  // Pair это std::tuple<const int&, const std::string&> 
}
std::vector<int> SomeValues = ...;
std::list<int> SomeOtherValues = ...;

for (int Value : chain(SomeValues, SomeOtherValues)) {
  // Сначала идут значения из SomeValues, а потом - из SomeOtherValues
}

Реализация

Реализацию zip можно найти в бусте. chain же в целом не представляет большой сложности в реализации.

tomilov commented 3 years ago

Как пример, можно ещё рассмотреть zip_iterator в Thrust.

nitrofox commented 3 years ago

Это все есть в range. И даже намного больше. godbolt

#include <fmt/format.h>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip.hpp>
#include <vector>
namespace rv = ranges::views;
int main() {
  std::vector<int> vi{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  std::vector<std::string> const vs{"hello", "goodbye", "hello", "goodbye"};

  for (auto [n, s] : rv::zip(vi, vs)) {
    fmt::print("n: {} | s: {}\n", n, s);
  }
  fmt::print("\n=====\n\n");
  for (auto [n, s] : rv::zip(rv::iota(4), vs)) {
    fmt::print("n: {} | s: {}\n", n, s);
  }
  fmt::print("\n=====\n\n");
  for (auto [n, s] :
       rv::zip(rv::iota(1), rv::concat(vs, vi | rv::transform([](auto i) {
                                             return std::to_string(i);
                                           })))) {
    fmt::print("n: {} | s: {}\n", n, s);
  }
}
apolukhin commented 2 years ago

zip приняли в C++23 в https://wg21.link/P2321

apolukhin commented 2 years ago

chain приняли в стандарт под именем join

#include <iostream>
#include <ranges>

int main()
{
    using namespace std::literals;
    auto v = { std::views::iota(1, 5), std::views::iota(5, 10) };
    auto jv = std::ranges::join_view(v);
    // 1 2 3 4 5 6 7 8 9
    for (int const e : jv) std::cout << e << ' ';
    std::cout << '\n';
}